001/* 002 * SPDX-License-Identifier: Apache-2.0 003 * 004 * Copyright 2023-2026 The Enola <https://enola.dev> Authors 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package dev.enola.core.rosetta; 019 020import com.google.common.collect.ImmutableList; 021import com.google.protobuf.Descriptors.Descriptor; 022 023import dev.enola.common.context.TLC; 024import dev.enola.common.convert.ConversionException; 025import dev.enola.common.io.resource.ReadableResource; 026import dev.enola.common.io.resource.ResourceProvider; 027import dev.enola.common.io.resource.WritableResource; 028import dev.enola.common.io.resource.convert.CharResourceConverter; 029import dev.enola.common.io.resource.convert.ResourceConverter; 030import dev.enola.common.io.resource.convert.ResourceConverterChain; 031import dev.enola.common.protobuf.DescriptorProvider; 032import dev.enola.common.protobuf.MessageResourceConverter; 033import dev.enola.common.protobuf.ProtoIO; 034import dev.enola.common.protobuf.YamlJsonResourceConverter; 035import dev.enola.data.iri.NamespaceConverter; 036import dev.enola.format.tika.rdf.TikaResourceIntoRdfResourceConverter; 037import dev.enola.format.xml.XmlResourceConverter; 038import dev.enola.rdf.io.RdfResourceConverter; 039import dev.enola.thing.gen.gexf.GexfGenerator; 040import dev.enola.thing.gen.gexf.GexfResourceConverter; 041import dev.enola.thing.gen.graphcommons.GraphCommonsJsonGenerator; 042import dev.enola.thing.gen.graphcommons.GraphCommonsResourceConverter; 043import dev.enola.thing.gen.graphviz.GraphvizGenerator; 044import dev.enola.thing.gen.graphviz.GraphvizResourceConverter; 045import dev.enola.thing.io.Loader; 046import dev.enola.thing.metadata.ThingMetadataProvider; 047import dev.enola.thing.repo.ThingProvider; 048 049import java.io.IOException; 050 051/** 052 * <a href="https://en.wikipedia.org/wiki/Rosetta_Stone">Rosetta Stone</a> for converting between 053 * different model resource and other formats. 054 */ 055public class Rosetta implements ResourceConverter { 056 057 // TODO Merge this with Canonicalizer (and move into dev.enola.common.canonicalize) 058 059 // This class orig. was in dev.enola.core.rosetta originally, in order to have classpath access 060 // to Entities.getDescriptor() and EntityKinds.getDescriptor() in the lookupDescriptor() below. 061 // But all of this has meanwhile been removed, so this could now moved e.g. to a new 062 // dev.enola[.common?].rosetta instead, and use a DescriptorProvider as generic proto Descriptor 063 // registry. TODO Move dev.enola.core.rosetta to dev.enola.action.rosetta? 064 065 private final ProtoIO protoIO = new ProtoIO(); 066 067 private static final DescriptorProvider DESCRIPTOR_PROVIDER = 068 new DescriptorProvider() { 069 070 @Override 071 public Descriptor findByName(String messageTypeURL) { 072 throw new IllegalArgumentException( 073 "TODO Cannot find Descriptor for: " + messageTypeURL); 074 } 075 076 @Override 077 public Descriptor getDescriptorForTypeUrl(String protoMessageFullyQualifiedName) { 078 throw new UnsupportedOperationException("Unimplemented method 'findByName'"); 079 } 080 }; 081 082 private final MessageResourceConverter messageResourceConverter = 083 new MessageResourceConverter(protoIO, DESCRIPTOR_PROVIDER); 084 085 private final ResourceConverterChain resourceConverterChain; 086 private final ResourceProvider rp; 087 088 public Rosetta(ResourceProvider rp, Loader loader) { 089 this.rp = rp; 090 // TODO Remove! 091 var tmp = new ThingMetadataProvider(ThingProvider.CTX, NamespaceConverter.CTX); 092 this.resourceConverterChain = 093 new ResourceConverterChain( 094 ImmutableList.of( 095 // TODO Use ServiceLoader with @AutoService 096 new RdfResourceIntoProtoThingResourceConverter(rp), 097 new RdfResourceConverter(rp), 098 new TikaResourceIntoRdfResourceConverter(rp), 099 messageResourceConverter, 100 new YamlJsonResourceConverter(), 101 new GraphvizResourceConverter(loader, new GraphvizGenerator(tmp)), 102 new GexfResourceConverter(loader, new GexfGenerator(tmp)), 103 new GraphCommonsResourceConverter( 104 loader, new GraphCommonsJsonGenerator(tmp)), 105 new XmlResourceConverter(rp), 106 new CharResourceConverter())); 107 // NOT new IdempotentCopyingResourceNonConverter() 108 } 109 110 @Override 111 public boolean convertInto(ReadableResource from, WritableResource into) 112 throws ConversionException, IOException { 113 try (var ctx = TLC.open()) { 114 ctx.push(ResourceProvider.class, rp); 115 if (!resourceConverterChain.convertInto(from, into)) { 116 throw new ConversionException( 117 "No Converter (registered on the Chain) accepted to transform from " 118 + from 119 + " into " 120 + into); 121 } 122 return true; 123 } 124 } 125}