Skip to content

Commit

Permalink
Editor / Improve support of schema having sibling elements for transl…
Browse files Browse the repository at this point in the history
…ation (#8540)

* Metadata editor / Support add embedded multilingual elements for metadata schemas that require to duplicate the element for each language

* Metadata editor / API / Ordering up and down / Add support for plugin
without container element (eg. DCAT-AP).

Current API only support to move one element up or down.

In DCAT-AP, multilingual record requires to move a group of element
containing all translations up or down.

* Metadata editor / Support add embedded multilingual / Simplify XPath and sonarlint.

* Metadata editor / Support add embedded multilingual / Check if a metadata schema is multilingual before doing a cast to MultilingualSchemaPlugin

---------

Co-authored-by: Jose García <[email protected]>
  • Loading branch information
fxprunayre and josegar74 authored Dec 11, 2024
1 parent 3389c9e commit 96869d8
Show file tree
Hide file tree
Showing 6 changed files with 304 additions and 50 deletions.
66 changes: 54 additions & 12 deletions core/src/main/java/org/fao/geonet/kernel/EditLib.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//=== EditLib
//===
//=============================================================================
//=== Copyright (C) 2001-2007 Food and Agriculture Organization of the
//=== Copyright (C) 2001-2024 Food and Agriculture Organization of the
//=== United Nations (FAO-UN), United Nations World Food Programme (WFP)
//=== and United Nations Environment Programme (UNEP)
//===
Expand Down Expand Up @@ -35,7 +35,6 @@
import java.io.StringReader;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.commons.collections.CollectionUtils;
Expand All @@ -50,6 +49,7 @@
import org.fao.geonet.kernel.schema.MetadataAttribute;
import org.fao.geonet.kernel.schema.MetadataSchema;
import org.fao.geonet.kernel.schema.MetadataType;
import org.fao.geonet.kernel.schema.MultilingualSchemaPlugin;
import org.fao.geonet.kernel.schema.SchemaPlugin;
import org.fao.geonet.utils.Xml;
import org.jaxen.JaxenException;
Expand Down Expand Up @@ -223,6 +223,47 @@ public Element addElement(MetadataSchema mdSchema, Element el, String qname) thr
return child;
}

/**
* Creates an element with a name (qname) as child of an element (el).
*
* If the element to create is multilingual and the related metadata schemas requires to duplicate it,
* one child per metadata language is added.
*
* See {@link org.fao.geonet.kernel.schema.MultilingualSchemaPlugin#duplicateElementsForMultilingual()}
*
* @param mdSchema Metadata schema
* @param el Parent element to add the new element.
* @param qname Name of the new element to add.
* @param languages Languages to add to the new elements.
* @return List of child elements added. For non-multilingual, contains 1 element.
* @throws Exception
*/
public List<Element> addElements(MetadataSchema mdSchema, Element el,
String qname, List<String> languages) throws Exception {

List<Element> result = new ArrayList<>();

if (mdSchema.getSchemaPlugin() instanceof MultilingualSchemaPlugin) {
MultilingualSchemaPlugin multilingualSchemaPlugin = (MultilingualSchemaPlugin) mdSchema.getSchemaPlugin();

if (!languages.isEmpty() &&
multilingualSchemaPlugin.duplicateElementsForMultilingual() &&
multilingualSchemaPlugin.isMultilingualElementType(mdSchema.getElementType(qname, el.getName()))) {
for(String language : languages) {
Element child = addElement(mdSchema, el, qname);
((MultilingualSchemaPlugin) mdSchema.getSchemaPlugin()).addTranslationToElement(child, language, "");
result.add(child);
}
return result;
}
}

// If no multilingual management is required, process the single element.
result.add(addElement(mdSchema, el, qname));

return result;
}

/**
* Adds XML fragment to the metadata record in the last element of the type of the element in
* its parent.
Expand Down Expand Up @@ -968,7 +1009,7 @@ public void clearVersion(String id) {
//--------------------------------------------------------------------------

private List<Element> filterOnQname(List<Element> children, String qname) {
Vector<Element> result = new Vector<Element>();
Vector<Element> result = new Vector<>();
for (Element child : children) {
if (child.getQualifiedName().equals(qname)) {
result.add(child);
Expand Down Expand Up @@ -1166,7 +1207,7 @@ public List<Element> searchChildren(String chName, Element md, String schema) th
//

boolean hasContent = false;
Vector<Element> holder = new Vector<Element>();
Vector<Element> holder = new Vector<>();

MetadataSchema mdSchema = scm.getSchema(schema);
String chUQname = getUnqualifiedName(chName);
Expand Down Expand Up @@ -1222,12 +1263,12 @@ public void expandElements(String schema, Element md) throws Exception {
MetadataType thisType = mdSchema.getTypeInfo(typeName);

if (thisType.hasContainers) {
Vector<Content> holder = new Vector<Content>();
Vector<Content> holder = new Vector<>();

for (String chName: thisType.getAlElements()) {
if (edit_CHOICE_GROUP_SEQUENCE_in(chName)) {
List<Element> elems = searchChildren(chName, md, schema);
if (elems.size() > 0) {
if (!elems.isEmpty()) {
holder.addAll(elems);
}
} else {
Expand All @@ -1246,7 +1287,7 @@ public void expandElements(String schema, Element md) throws Exception {
* For each container element - descend and collect children.
*/
private Vector<Object> getContainerChildren(Element md) {
Vector<Object> result = new Vector<Object>();
Vector<Object> result = new Vector<>();

@SuppressWarnings("unchecked")
List<Element> chChilds = md.getChildren();
Expand All @@ -1268,17 +1309,17 @@ private Vector<Object> getContainerChildren(Element md) {
public void contractElements(Element md) {
//--- contract container children at each level in the XML tree

Vector<Object> children = new Vector<Object>();
Vector<Object> children = new Vector<>();
@SuppressWarnings("unchecked")
List<Content> childs = md.getContent();
for (Content obj : childs) {
if (obj instanceof Element) {
Element mdCh = (Element) obj;
String mdName = mdCh.getName();
if (edit_CHOICE_GROUP_SEQUENCE_in(mdName)) {
if (mdCh.getChildren().size() > 0) {
if (!mdCh.getChildren().isEmpty()) {
Vector<Object> chChilds = getContainerChildren(mdCh);
if (chChilds.size() > 0) {
if (!chChilds.isEmpty()) {
children.addAll(chChilds);
}
}
Expand Down Expand Up @@ -1525,7 +1566,7 @@ private void insertLast(Element md, String childName, String childNS, Element ch
@SuppressWarnings("unchecked")
List<Element> list = md.getChildren();

List<Element> v = new ArrayList<Element>();
List<Element> v = new ArrayList<>();

for (int i = 0; i < list.size(); i++) {
Element el = list.get(i);
Expand Down Expand Up @@ -1606,7 +1647,8 @@ public Element createElement(String schema, Element child, Element parent) throw
MetadataSchema mds = scm.getSchema(schema);
MetadataType mdt = getType(mds, parent);

int min = -1, max = -1;
int min = -1;
int max = -1;

for (int i = 0; i < mdt.getElementCount(); i++) {
if (childQName.equals(mdt.getElementAt(i))) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.fao.geonet.kernel.schema.LinkPatternStreamer.RawLinkPatternStreamer;
import org.fao.geonet.utils.Log;
import org.fao.geonet.utils.Xml;
import org.jdom.Attribute;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
Expand Down Expand Up @@ -579,6 +580,31 @@ public Element processElement(Element el,

}

@Override
public boolean duplicateElementsForMultilingual() {
return false;
}

@Override
public List<String> getMetadataLanguages(Element metadata) {
try {
return Xml.selectNodes(metadata, ".//*[name() = 'mdb:defaultLocale' or name() = 'mdb:otherLocale']/lan:PT_Locale/@id", allNamespaces.asList())
.stream()
.filter(Attribute.class::isInstance)
.map(node -> ((Attribute)node).getValue())
.filter(s -> s != null && !s.isBlank())
.collect(Collectors.toList());
} catch (JDOMException ignored) {
}
return Collections.emptyList();
}

@Override
public boolean isMultilingualElementType(String elementType) {
// Not required in ISO schemas, only required for schemas where duplicateElementsForMultilingual returns true.
return false;
}

/**
* Checks if an element requires processing in {@link #processElement(Element, String, String, String)}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,31 @@ public Element processElement(Element el,

}

@Override
public boolean duplicateElementsForMultilingual() {
return false;
}

@Override
public List<String> getMetadataLanguages(Element metadata) {
try {
return Xml.selectNodes(metadata, ".//gmd:locale/gmd:PT_Locale/@id", allNamespaces.asList())
.stream()
.filter(Attribute.class::isInstance)
.map(node -> ((Attribute)node).getValue())
.filter(s -> s != null && !s.isBlank())
.collect(Collectors.toList());
} catch (JDOMException ignored) {
}
return Collections.emptyList();
}

@Override
public boolean isMultilingualElementType(String elementType) {
// Not required in ISO schemas, only required for schemas where duplicateElementsForMultilingual returns true.
return false;
}

/**
* Checks if an element requires processing in {@link #processElement(Element, String, String, String)}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.jdom.Element;
import org.jdom.JDOMException;

import java.util.ArrayList;
import java.util.List;

/**
Expand All @@ -40,8 +41,66 @@ public interface MultilingualSchemaPlugin {
*/
public abstract List<Element> getTranslationForElement(Element element, String languageIdentifier);

/**
* Updates an element with the related multilingual information using the language and value provided.
*
* @param element XML element to update.
* @param languageIdentifier Language identifier.
* @param value Value for the element.
*/
public abstract void addTranslationToElement(Element element, String languageIdentifier, String value);

/**
* Remove all multilingual aspect of an element.
*
* @param element XML element to update.
* @param mdLang Metadata languages.
* @return
* @throws JDOMException
*/
public abstract Element removeTranslationFromElement(Element element, List<String> mdLang) throws JDOMException;

/**
* Retrieves the list of metadata languages used in the metadata.
* @param metadata
* @return
*/
public abstract List<String> getMetadataLanguages(Element metadata);

/**
* Checks if an element type is multilingual. For example, in DCAT schema, rdf:PlainLiteral type.
*
* @param elementType Element type to check.
* @return true if the element type is multilingual, otherwise false.
*/
public abstract boolean isMultilingualElementType(String elementType);


/**
* Flag to indicate when adding an element to the metadata editor, if should be duplicated for each metadata language.
* For example, in DCAT schema adding vcard:organization-name in a metadata that has English and French languages
* (similar case for Dublin Core), should duplicate the element for each language:
*
* <vcard:organization-name xml:lang="en"/>
* <vcard:organization-name xml:lang="fr"/>
*
* For ISO profiles should be set to false, as the multilingual elements are not duplicated. Multilingual values
* are added as children elements, requiring a different processing. Adding gmd:organisationName in a metadata
* that has English and French languages:
*
* <gmd:organisationName xsi:type="gmd:PT_FreeText_PropertyType">
* <gco:CharacterString></gco:CharacterString>
* <gmd:PT_FreeText>
* <gmd:textGroup>
* <gmd:LocalisedCharacterString locale="#EN"></gmd:LocalisedCharacterString>
* </gmd:textGroup>
* <gmd:textGroup>
* <gmd:LocalisedCharacterString locale="#FR"></gmd:LocalisedCharacterString>
* </gmd:textGroup>
* </gmd:PT_FreeText>
* </gmd:organisationName>
*
* @return
*/
public abstract boolean duplicateElementsForMultilingual();
}
Loading

0 comments on commit 96869d8

Please sign in to comment.