diff --git a/src/main/java/net/rptools/maptool/client/functions/TokenCopyDeleteFunctions.java b/src/main/java/net/rptools/maptool/client/functions/TokenCopyDeleteFunctions.java index 844b369057..3297c5eb65 100644 --- a/src/main/java/net/rptools/maptool/client/functions/TokenCopyDeleteFunctions.java +++ b/src/main/java/net/rptools/maptool/client/functions/TokenCopyDeleteFunctions.java @@ -33,6 +33,7 @@ import net.rptools.maptool.model.TokenFootprint; import net.rptools.maptool.model.Zone; import net.rptools.maptool.model.ZonePoint; +import net.rptools.maptool.util.AssetResolver; import net.rptools.maptool.util.FunctionUtil; import net.rptools.parser.Parser; import net.rptools.parser.ParserException; @@ -119,6 +120,10 @@ private String createToken(MapToolVariableResolver resolver, JsonObject vals) throw new ParserException(I18N.getText("macro.function.tokenCopyDelete.noImage")); } String tokenImage = vals.get("tokenImage").getAsString(); + var asset = new AssetResolver().getAssetKey(tokenImage); + if (asset.isPresent()) { + tokenImage = asset.get().toString(); + } Zone zone = MapTool.getFrame().getCurrentZoneRenderer().getZone(); List allTokens = zone.getTokens(); diff --git a/src/main/java/net/rptools/maptool/client/functions/TokenImage.java b/src/main/java/net/rptools/maptool/client/functions/TokenImage.java index 0a002006c3..c095e8aa23 100644 --- a/src/main/java/net/rptools/maptool/client/functions/TokenImage.java +++ b/src/main/java/net/rptools/maptool/client/functions/TokenImage.java @@ -18,8 +18,6 @@ import java.awt.Image; import java.math.BigDecimal; import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import net.rptools.lib.MD5Key; import net.rptools.maptool.client.MapTool; import net.rptools.maptool.client.ui.zone.ZoneRenderer; @@ -27,6 +25,7 @@ import net.rptools.maptool.model.Asset; import net.rptools.maptool.model.AssetManager; import net.rptools.maptool.model.Token; +import net.rptools.maptool.util.AssetResolver; import net.rptools.maptool.util.FunctionUtil; import net.rptools.maptool.util.ImageManager; import net.rptools.parser.Parser; @@ -39,8 +38,6 @@ public class TokenImage extends AbstractFunction { /** Singleton instance. */ private static final TokenImage instance = new TokenImage(); - private static final Pattern assetRE = Pattern.compile("asset://([^-]+)"); - enum imageType { TOKEN_IMAGE(0), TOKEN_PORTRAIT(1), @@ -260,12 +257,8 @@ private String typeOf(Object ob) { * @throws ParserException if assetName not found or assetName doesn't */ public static MD5Key getMD5Key(String assetName, String functionName) throws ParserException { - Matcher m = assetRE.matcher(assetName); - - String assetId; - if (m.matches()) { - assetId = m.group(1); - } else if (assetName.toLowerCase().startsWith("image:")) { + String assetId = null; + if (assetName.toLowerCase().startsWith("image:")) { Token imageToken = findImageToken(assetName, functionName); if (imageToken == null) { throw new ParserException( @@ -273,10 +266,17 @@ public static MD5Key getMD5Key(String assetName, String functionName) throws Par } assetId = imageToken.getImageAssetId().toString(); } else { + var assetKey = new AssetResolver().getAssetKey(assetName); + if (assetKey.isPresent()) { + assetId = assetKey.get().toString(); + } + } + if (assetId == null) { throw new ParserException( I18N.getText("macro.function.general.argumentTypeInvalid", functionName, 1, assetName)); + } else { + return new MD5Key(assetId); } - return new MD5Key(assetId); } private static void setImage(Token token, String assetName) throws ParserException { diff --git a/src/main/java/net/rptools/maptool/model/library/Library.java b/src/main/java/net/rptools/maptool/model/library/Library.java index a162ce7f6c..aede485c3f 100644 --- a/src/main/java/net/rptools/maptool/model/library/Library.java +++ b/src/main/java/net/rptools/maptool/model/library/Library.java @@ -21,6 +21,7 @@ import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import net.rptools.lib.MD5Key; import net.rptools.maptool.client.MapToolMacroContext; import net.rptools.maptool.model.Asset; import net.rptools.maptool.model.Token; @@ -48,6 +49,23 @@ public interface Library { */ CompletableFuture locationExists(URL location) throws IOException; + /** + * Checks to see if the specified location is an Asset. + * + * @param location the location to check. + * @return {@code true} if the location is an asset, otherwise {@code false}. + */ + CompletableFuture isAsset(URL location); + + /** + * Returns the asset at the specified location. This will only return a value if {@link + * #isAsset(URL)} returns {@code true}. + * + * @param location the location to get the asset for. + * @return the asset at the specified location. + */ + CompletableFuture> getAssetKey(URL location); + /** * Reads the location as a string. * diff --git a/src/main/java/net/rptools/maptool/model/library/addon/AddOnLibrary.java b/src/main/java/net/rptools/maptool/model/library/addon/AddOnLibrary.java index aee3c7fb0f..2be092f6e8 100644 --- a/src/main/java/net/rptools/maptool/model/library/addon/AddOnLibrary.java +++ b/src/main/java/net/rptools/maptool/model/library/addon/AddOnLibrary.java @@ -439,6 +439,20 @@ public CompletableFuture locationExists(URL location) throws IOExceptio } } + @Override + public CompletableFuture isAsset(URL location) { + return CompletableFuture.completedFuture(getURILocation(location) != null); + } + + @Override + public CompletableFuture> getAssetKey(URL location) { + var AssetInfo = getURILocation(location); + if (AssetInfo == null) { + return CompletableFuture.completedFuture(Optional.empty()); + } + return CompletableFuture.completedFuture(Optional.of(AssetInfo.getValue0())); + } + @Override public CompletableFuture readAsString(URL location) throws IOException { if (allowsUriAccess) { diff --git a/src/main/java/net/rptools/maptool/model/library/builtin/MapToolBuiltInLibrary.java b/src/main/java/net/rptools/maptool/model/library/builtin/MapToolBuiltInLibrary.java index a26ae9643b..0de2893dce 100644 --- a/src/main/java/net/rptools/maptool/model/library/builtin/MapToolBuiltInLibrary.java +++ b/src/main/java/net/rptools/maptool/model/library/builtin/MapToolBuiltInLibrary.java @@ -24,6 +24,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; +import net.rptools.lib.MD5Key; import net.rptools.maptool.client.AppConstants; import net.rptools.maptool.client.MapTool; import net.rptools.maptool.client.MapToolMacroContext; @@ -109,6 +110,16 @@ public CompletableFuture locationExists(URL location) throws IOExceptio return CompletableFuture.completedFuture(resourcesMap.containsKey(key)); } + @Override + public CompletableFuture isAsset(URL location) { + return CompletableFuture.completedFuture(false); + } + + @Override + public CompletableFuture> getAssetKey(URL location) { + return CompletableFuture.completedFuture(Optional.empty()); + } + @Override public CompletableFuture readAsString(URL location) throws IOException { var path = location.getPath().replaceFirst("^/", ""); diff --git a/src/main/java/net/rptools/maptool/model/library/token/LibraryToken.java b/src/main/java/net/rptools/maptool/model/library/token/LibraryToken.java index 1cfa5ee972..446be2c6d8 100644 --- a/src/main/java/net/rptools/maptool/model/library/token/LibraryToken.java +++ b/src/main/java/net/rptools/maptool/model/library/token/LibraryToken.java @@ -28,6 +28,7 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.stream.Stream; +import net.rptools.lib.MD5Key; import net.rptools.maptool.client.MapTool; import net.rptools.maptool.client.MapToolMacroContext; import net.rptools.maptool.language.I18N; @@ -182,6 +183,16 @@ public CompletableFuture locationExists(URL location) throws IOExceptio }); } + @Override + public CompletableFuture isAsset(URL location) { + return CompletableFuture.completedFuture(Boolean.FALSE); + } + + @Override + public CompletableFuture> getAssetKey(URL location) { + return CompletableFuture.completedFuture(Optional.empty()); + } + @Override public CompletableFuture readAsString(URL location) throws IOException { final var loc = Location.getLocation(location); diff --git a/src/main/java/net/rptools/maptool/util/AssetResolver.java b/src/main/java/net/rptools/maptool/util/AssetResolver.java new file mode 100644 index 0000000000..1ca730e216 --- /dev/null +++ b/src/main/java/net/rptools/maptool/util/AssetResolver.java @@ -0,0 +1,90 @@ +/* + * This software Copyright by the RPTools.net development team, and + * licensed under the Affero GPL Version 3 or, at your option, any later + * version. + * + * MapTool Source Code is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public + * License * along with this source Code. If not, please visit + * and specifically the Affero license + * text at . + */ +package net.rptools.maptool.util; + +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import net.rptools.lib.MD5Key; +import net.rptools.maptool.model.library.Library; +import net.rptools.maptool.model.library.LibraryManager; + +/** + * Utility Class to aid in resolving asset keys for images. It will resolve the asset key for the + * following: lib:// URI asset:// URI image:token MD5 hash (as String) + */ +public class AssetResolver { + + /** + * Returns the asset key for the specified URL. + * + * @param url the URL to get the asset key for. + * @return the MD5key of the asset at the specified URL. + */ + public Optional getAssetKey(URL url) { + Optional lib = null; + try { + lib = new LibraryManager().getLibrary(url).get(); + if (lib.isPresent()) { + var asset = lib.get().getAssetKey(url).get(); + if (asset.isPresent()) { + String key = asset.get().toString(); + return Optional.of(new MD5Key(key)); + } + } + } catch (InterruptedException | ExecutionException e) { + return Optional.empty(); + } + return Optional.empty(); + } + + /** + * Returns the asset key for the specified location. Locations can be: lib:// URI asset:// URI + * image:token MD5 hash (as String) + * + * @param location the location to get the asset key for. + * @return the MD5key of the asset at the specified location. + */ + public Optional getAssetKey(String location) { + if (location.startsWith("asset://")) { + return getAssetKey(location.substring(8)); + } else if (location.toLowerCase().startsWith("lib:")) { + try { + return getAssetKey(new URI(location)); + } catch (URISyntaxException e) { + return Optional.empty(); + } + } else { + return Optional.of(new MD5Key(location)); + } + } + + /** + * Returns the asset key for the specified URI. + * + * @param uri the URI to get the asset key for. + * @return the MD5key of the asset at the specified URI. + */ + public Optional getAssetKey(URI uri) { + try { + return getAssetKey(uri.toURL()); + } catch (MalformedURLException e) { + return Optional.empty(); + } + } +}