/* * Copyright 2014 MovingBlocks * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.terasology.rendering.nui.skin; import com.google.common.base.Charsets; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonParseException; import com.google.gson.stream.JsonReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.terasology.assets.ResourceUrn; import org.terasology.assets.format.AbstractAssetFileFormat; import org.terasology.assets.format.AssetDataFile; import org.terasology.assets.module.annotations.RegisterAssetFileFormat; import org.terasology.persistence.ModuleContext; import org.terasology.persistence.typeHandling.extensionTypes.ColorTypeHandler; import org.terasology.persistence.typeHandling.gson.JsonTypeHandlerAdapter; import org.terasology.reflection.metadata.ClassLibrary; import org.terasology.reflection.metadata.ClassMetadata; import org.terasology.registry.CoreRegistry; import org.terasology.rendering.assets.font.Font; import org.terasology.rendering.assets.texture.TextureRegion; import org.terasology.rendering.nui.Color; import org.terasology.rendering.nui.NUIManager; import org.terasology.rendering.nui.UIWidget; import org.terasology.utilities.Assets; import org.terasology.utilities.gson.AssetTypeAdapter; import org.terasology.utilities.gson.CaseInsensitiveEnumTypeAdapterFactory; import java.io.IOException; import java.io.InputStreamReader; import java.lang.reflect.Type; import java.util.List; import java.util.Map; import java.util.Optional; /** */ @RegisterAssetFileFormat public class UISkinFormat extends AbstractAssetFileFormat<UISkinData> { private static final Logger logger = LoggerFactory.getLogger(UISkinFormat.class); private Gson gson; public UISkinFormat() { super("skin"); gson = new GsonBuilder() .registerTypeAdapterFactory(new CaseInsensitiveEnumTypeAdapterFactory()) .registerTypeAdapter(Font.class, new AssetTypeAdapter<>(Font.class)) .registerTypeAdapter(UISkinData.class, new UISkinTypeAdapter()) .registerTypeAdapter(TextureRegion.class, new TextureRegionTypeAdapter()) .registerTypeAdapter(Color.class, new JsonTypeHandlerAdapter<>(new ColorTypeHandler())) .registerTypeAdapter(Optional.class, new OptionalTextureRegionTypeAdapter()) .create(); } @Override public UISkinData load(ResourceUrn urn, List<AssetDataFile> inputs) throws IOException { try (JsonReader reader = new JsonReader(new InputStreamReader(inputs.get(0).openStream(), Charsets.UTF_8))) { reader.setLenient(true); UISkinData data = gson.fromJson(reader, UISkinData.class); data.setSource(inputs.get(0)); return data; } catch (JsonParseException e) { throw new IOException("Failed to load skin '" + urn + "'", e); } } public UISkinData load(JsonElement element) throws IOException { return gson.fromJson(element, UISkinData.class); } private static class TextureRegionTypeAdapter implements JsonDeserializer<TextureRegion> { @Override public TextureRegion deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { String uri = json.getAsString(); return Assets.getTextureRegion(uri).orElse(null); } } private static class UISkinTypeAdapter implements JsonDeserializer<UISkinData> { @Override public UISkinData deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { if (json.isJsonObject()) { UISkinBuilder builder = new UISkinBuilder(); DefaultInfo defaultInfo = context.deserialize(json, DefaultInfo.class); defaultInfo.apply(builder); return builder.build(); } return null; } } private static class DefaultInfo extends FamilyInfo { public String inherit; public Map<String, FamilyInfo> families; @Override public void apply(UISkinBuilder builder) { super.apply(builder); if (inherit != null) { Optional<? extends UISkin> skin = Assets.get(inherit, UISkin.class); if (skin.isPresent()) { builder.setBaseSkin(skin.get()); } } if (families != null) { for (Map.Entry<String, FamilyInfo> entry : families.entrySet()) { builder.setFamily(entry.getKey()); entry.getValue().apply(builder); } } } } private static class FamilyInfo extends StyleInfo { public Map<String, ElementInfo> elements; public void apply(UISkinBuilder builder) { super.apply(builder); if (elements != null) { for (Map.Entry<String, ElementInfo> entry : elements.entrySet()) { ClassLibrary<UIWidget> library = CoreRegistry.get(NUIManager.class).getWidgetMetadataLibrary(); ClassMetadata<? extends UIWidget, ?> metadata = library.resolve(entry.getKey(), ModuleContext.getContext()); if (metadata != null) { builder.setElementClass(metadata.getType()); entry.getValue().apply(builder); } else { logger.warn("Failed to resolve UIWidget class {}, skipping style information", entry.getKey()); } } } } } private static class PartsInfo extends StyleInfo { public Map<String, StyleInfo> modes; public void apply(UISkinBuilder builder) { super.apply(builder); if (modes != null) { for (Map.Entry<String, StyleInfo> entry : modes.entrySet()) { builder.setElementMode(entry.getKey()); entry.getValue().apply(builder); } } } } private static class ElementInfo extends StyleInfo { public Map<String, PartsInfo> parts; public Map<String, StyleInfo> modes; public void apply(UISkinBuilder builder) { super.apply(builder); if (modes != null) { for (Map.Entry<String, StyleInfo> entry : modes.entrySet()) { builder.setElementMode(entry.getKey()); entry.getValue().apply(builder); } } if (parts != null) { for (Map.Entry<String, PartsInfo> entry : parts.entrySet()) { builder.setElementPart(entry.getKey()); entry.getValue().apply(builder); } } } } private static class StyleInfo extends UIStyleFragment { private void apply(UISkinBuilder builder) { builder.setStyleFragment(this); } } private static class OptionalTextureRegionTypeAdapter implements JsonDeserializer<Optional<?>> { @Override public Optional<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { return Assets.getTextureRegion(json.getAsString()); } } }