@@ -36,7 +36,9 @@ public class ClasspathManager {
3636 public enum Scope {
3737 CORE ,
3838 CUSTOM ,
39- PLUGINS ,;
39+ PLUGINS ,
40+ LOCAL_LIBRARIES ,
41+ LOCAL_CLASSES ;
4042
4143 @ NotNull
4244 public static final Set <Scope > ONLY_CORE = EnumSet .of (CORE );
@@ -83,6 +85,18 @@ public static ClasspathManager getInstance() {
8385 @ Nullable
8486 private volatile ClassPathScanner customScanner ;
8587
88+ /**
89+ * The local libraries scanner.
90+ */
91+ @ Nullable
92+ private volatile ClassPathScanner localLibrariesScanner ;
93+
94+ /**
95+ * The local classes scanner.
96+ */
97+ @ Nullable
98+ private volatile ClassPathScanner localClassesScanner ;
99+
86100 /**
87101 * The libraries class loader.
88102 */
@@ -151,16 +165,16 @@ public synchronized void reload() {
151165
152166 final URLClassLoader librariesLoader = getLibrariesLoader ();
153167 final URLClassLoader classesLoader = getClassesLoader ();
154- final ClassLoader classLoader = classesLoader == null ? librariesLoader : classesLoader ;
155-
156- final ClassPathScanner scanner = classesLoader == null ? ClassPathScannerFactory .newDefaultScanner () :
157- ClassPathScannerFactory .newDefaultScanner (classLoader );
158168
159169 if (librariesLoader == null && classesLoader == null ) {
160- this .customScanner = scanner ;
170+ this .customScanner = null ;
161171 return ;
162172 }
163173
174+ final ClassLoader classLoader = classesLoader == null ? librariesLoader : classesLoader ;
175+ final ClassPathScanner scanner = classesLoader == null ? ClassPathScannerFactory .newDefaultScanner () :
176+ ClassPathScannerFactory .newDefaultScanner (classLoader );
177+
164178 final Array <URL > urls = ArrayFactory .newArray (URL .class );
165179
166180 if (librariesLoader != null ) {
@@ -178,7 +192,7 @@ public synchronized void reload() {
178192
179193 scanner .addAdditionalPaths (paths );
180194 scanner .setUseSystemClasspath (false );
181- scanner .scan (path -> true );
195+ scanner .scan ();
182196
183197 this .customScanner = scanner ;
184198 }
@@ -202,13 +216,13 @@ private void updateLibraries() {
202216 if (path == null ) return ;
203217
204218 final Array <Path > jars = FileUtils .getFiles (path , false , JAR_EXTENSIONS );
205- final URL [] urls = jars .stream ().map (FileUtils ::toUrl )
219+ final URL [] urls = jars .stream ()
220+ .map (FileUtils ::toUrl )
206221 .toArray (URL []::new );
207222
208223 final URLClassLoader classLoader = new URLClassLoader (urls , getClass ().getClassLoader ());
209224
210225 assetManager .addClassLoader (classLoader );
211-
212226 setLibrariesLoader (classLoader );
213227 }
214228
@@ -224,7 +238,7 @@ private void updateClasses() {
224238
225239 if (currentClassLoader != null ) {
226240 assetManager .removeClassLoader (currentClassLoader );
227- setLibrariesLoader (null );
241+ setClassesLoader (null );
228242 }
229243
230244 final Path path = EDITOR_CONFIG .getClassesPath ();
@@ -241,18 +255,117 @@ private void updateClasses() {
241255 });
242256 });
243257
244- final URL [] urls = folders .stream ().map (FileUtils ::toUrl )
258+ final URL [] urls = folders .stream ()
259+ .map (FileUtils ::toUrl )
245260 .toArray (URL []::new );
246261
247262 final ClassLoader librariesLoader = getLibrariesLoader ();
248263 final ClassLoader parent = librariesLoader == null ? getClass ().getClassLoader () : librariesLoader ;
249264 final URLClassLoader classLoader = new URLClassLoader (urls , parent );
250265
251266 assetManager .addClassLoader (classLoader );
252-
253267 setClassesLoader (classLoader );
254268 }
255269
270+ /**
271+ * Load local libraries.
272+ */
273+ @ FromAnyThread
274+ public synchronized void loadLocalLibraries (@ NotNull final Array <Path > libraries ) {
275+
276+ final Editor editor = Editor .getInstance ();
277+ final AssetManager assetManager = editor .getAssetManager ();
278+ final URLClassLoader currentClassLoader = getLocalLibrariesLoader ();
279+
280+ if (currentClassLoader != null ) {
281+ assetManager .removeClassLoader (currentClassLoader );
282+ setLocalLibrariesLoader (null );
283+ }
284+
285+ if (libraries .isEmpty ()) {
286+ this .localLibrariesScanner = null ;
287+ return ;
288+ }
289+
290+ final URL [] urlArray = libraries .stream ()
291+ .map (FileUtils ::toUrl )
292+ .toArray (URL []::new );
293+
294+ final URLClassLoader classLoader = new URLClassLoader (urlArray , getClass ().getClassLoader ());
295+
296+ assetManager .addClassLoader (classLoader );
297+ setLocalLibrariesLoader (classLoader );
298+
299+ final ClassPathScanner scanner = ClassPathScannerFactory .newDefaultScanner (classLoader );
300+ final Array <URL > urls = ArrayFactory .asArray (classLoader .getURLs ());
301+ final String [] paths = urls .stream ().map (url -> Utils .get (url , URL ::toURI ))
302+ .map (Paths ::get )
303+ .map (Path ::toString )
304+ .toArray (String []::new );
305+
306+ scanner .addAdditionalPaths (paths );
307+ scanner .setUseSystemClasspath (false );
308+ scanner .scan ();
309+
310+ this .localLibrariesScanner = scanner ;
311+ }
312+
313+ /**
314+ * Load local classes.
315+ */
316+ @ FromAnyThread
317+ public synchronized void loadLocalClasses (@ Nullable final Path output ) {
318+
319+ final Editor editor = Editor .getInstance ();
320+ final AssetManager assetManager = editor .getAssetManager ();
321+ final URLClassLoader currentClassLoader = getLocalClassesLoader ();
322+
323+ if (currentClassLoader != null ) {
324+ assetManager .removeClassLoader (currentClassLoader );
325+ setLocalClassesLoader (null );
326+ }
327+
328+ if (output == null || !Files .exists (output )) {
329+ this .localClassesScanner = null ;
330+ return ;
331+ }
332+
333+ final Array <Path > folders = ArrayFactory .newArray (Path .class );
334+
335+ Utils .run (output , folders , (dir , toStore ) -> {
336+ final DirectoryStream <Path > stream = Files .newDirectoryStream (dir );
337+ stream .forEach (subFile -> {
338+ if (Files .isDirectory (subFile )) {
339+ toStore .add (subFile );
340+ }
341+ });
342+ });
343+
344+ final URL [] urlArray = folders .stream ()
345+ .map (FileUtils ::toUrl )
346+ .toArray (URL []::new );
347+
348+ final ClassLoader librariesLoader = getLocalLibrariesLoader ();
349+ final ClassLoader parent = librariesLoader == null ? getClass ().getClassLoader () : librariesLoader ;
350+ final URLClassLoader classLoader = new URLClassLoader (urlArray , parent );
351+
352+ assetManager .addClassLoader (classLoader );
353+ setLocalClassesLoader (classLoader );
354+
355+ final ClassPathScanner scanner = ClassPathScannerFactory .newDefaultScanner (classLoader );
356+ final Array <URL > urls = ArrayFactory .asArray (classLoader .getURLs ());
357+ final String [] paths = urls .stream ().map (url -> Utils .get (url , URL ::toURI ))
358+ .map (Paths ::get )
359+ .map (Path ::toString )
360+ .toArray (String []::new );
361+
362+ scanner .addAdditionalPaths (paths );
363+ scanner .setUseSystemClasspath (false );
364+ scanner .scan ();
365+
366+ this .localClassesScanner = scanner ;
367+ }
368+
256369 /**
257370 * @param librariesLoader the additional class loader.
258371 */
@@ -345,6 +458,24 @@ private void setLocalClassesLoader(@Nullable final URLClassLoader localClassesLo
345458 return customScanner ;
346459 }
347460
461+ /**
462+ * Get the local libraries scanner.
463+ *
464+ * @return the local libraries scanner.
465+ */
466+ private @ Nullable ClassPathScanner getLocalLibrariesScanner () {
467+ return localLibrariesScanner ;
468+ }
469+
470+ /**
471+ * Get the local classes scanner.
472+ *
473+ * @return the local classes scanner.
474+ */
475+ private @ Nullable ClassPathScanner getLocalClassesScanner () {
476+ return localClassesScanner ;
477+ }
478+
348479 /**
349480 * Find all implementations of the interface class.
350481 *
@@ -367,6 +498,16 @@ private void setLocalClassesLoader(@Nullable final URLClassLoader localClassesLo
367498 customScanner .findImplements (result , interfaceClass );
368499 }
369500
501+ final ClassPathScanner localLibrariesScanner = getLocalLibrariesScanner ();
502+ if (localLibrariesScanner != null && scope .contains (Scope .LOCAL_LIBRARIES )) {
503+ localLibrariesScanner .findImplements (result , interfaceClass );
504+ }
505+
506+ final ClassPathScanner localClassesScanner = getLocalClassesScanner ();
507+ if (localClassesScanner != null && scope .contains (Scope .LOCAL_CLASSES )) {
508+ localClassesScanner .findImplements (result , interfaceClass );
509+ }
510+
370511 if (scope .contains (Scope .PLUGINS )) {
371512 final PluginManager pluginManager = PluginManager .getInstance ();
372513 pluginManager .handlePlugins (plugin -> {
0 commit comments