blob: 31e6f15037c2f78b460d2cadde022d5f9237bd7b [file] [log] [blame]
/*
* Copyright (c) OSGi Alliance (2016). All Rights Reserved.
*
* 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.apache.felix.schematizer.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.apache.felix.schematizer.Node;
import org.apache.felix.schematizer.NodeVisitor;
import org.apache.felix.schematizer.Schema;
import org.osgi.util.converter.Converter;
import org.osgi.util.converter.Converters;
public class SchemaImpl implements Schema {
private final String name;
private final HashMap<String, NodeImpl> nodes = new LinkedHashMap<>();
public SchemaImpl(String aName) {
name = aName;
}
@Override
public String name() {
return name;
}
@Override
public Node rootNode() {
return rootNodeInternal();
}
public NodeImpl rootNodeInternal() {
return nodes.get("/");
}
@Override
public boolean hasNodeAtPath(String absolutePath) {
return nodes.containsKey(absolutePath);
}
@Override
public Node nodeAtPath( String absolutePath ) {
return nodes.get(absolutePath);
}
@Override
public Node parentOf( Node aNode ) {
if (aNode == null || aNode.absolutePath() == null)
return Node.ERROR;
NodeImpl node = nodes.get(aNode.absolutePath());
if (node == null)
return Node.ERROR;
return node.parent();
}
void add(NodeImpl node) {
nodes.put(node.absolutePath(), node);
}
void add(Map<String, NodeImpl> moreNodes) {
nodes.putAll(moreNodes);
}
@Override
public Map<String, Node.DTO> toMap() {
NodeImpl root = nodes.get("/");
Map<String, Node.DTO> m = new HashMap<>();
m.put("/",root.toDTO());
return m;
}
@SuppressWarnings( { "unchecked", "rawtypes" } )
Map<String, NodeImpl> toMapInternal() {
return (Map)nodes.clone();
}
@Override
public void visit(NodeVisitor visitor) {
nodes.values().stream().forEach(n -> visitor.apply(n));
}
@Override
public Collection<?> valuesAt(String path, Object object) {
final Converter converter = Converters.standardConverter();
@SuppressWarnings( "unchecked" )
final Map<String, Object> map = (Map<String, Object>)converter.convert(object).sourceAsDTO().to( Map.class );
if (map == null || map.isEmpty())
return Collections.emptyList();
if (path.startsWith("/"))
path = path.substring(1);
String[] pathParts = path.split("/");
if (pathParts.length <= 0)
return Collections.emptyList();
List<String> contexts = Arrays.asList(pathParts);
return valuesAt("", map, contexts, 0);
}
@SuppressWarnings( { "rawtypes", "unchecked" } )
private Collection<?> valuesAt(String context, Map<String, Object> objectMap, List<String> contexts, int currentIndex) {
List<Object> result = new ArrayList<>();
String currentContext = contexts.get(currentIndex);
if (objectMap == null)
return result;
Object o = objectMap.get(currentContext);
if (o instanceof List) {
List<Object> l = (List<Object>)o;
if (currentIndex == contexts.size() - 1) {
// We are at the end, so just add the collection
result.add(convertToType(pathFrom(contexts, 0), l));
return result;
}
currentContext = pathFrom(contexts, ++currentIndex);
for (Object o2 : l)
{
final Converter converter = Converters.standardConverter();
final Map<String, Object> m = (Map<String, Object>)converter.convert(o2).sourceAsDTO().to( Map.class );
result.addAll( valuesAt( currentContext, m, contexts, currentIndex ) );
}
} else if (o instanceof Map){
if (currentIndex == contexts.size() - 1) {
// We are at the end, so just add the result
result.add(convertToType(pathFrom(contexts, 0), (Map)o));
return result;
}
result.addAll(valuesAt( currentContext, (Map)o, contexts, ++currentIndex));
} else if (currentIndex < contexts.size() - 1) {
final Converter converter = Converters.standardConverter();
final Map<String, Object> m = (Map<String, Object>)converter.convert(o).sourceAsDTO().to(Map.class);
currentContext = pathFrom(contexts, ++currentIndex);
result.addAll(valuesAt( currentContext, m, contexts, currentIndex ));
} else {
result.add(o);
}
return result;
}
@SuppressWarnings( "rawtypes" )
private Object convertToType( String path, Map map ) {
if (!hasNodeAtPath(path))
return map;
Node node = nodeAtPath(path);
Object result = Converters.standardConverter().convert(map).targetAsDTO().to(node.type());
return result;
}
private List<?> convertToType( String path, List<?> list ) {
if (!hasNodeAtPath(path))
return list;
Node node = nodeAtPath(path);
return list.stream()
.map( v -> Converters.standardConverter().convert(v).sourceAsDTO().to(node.type()))
.collect( Collectors.toList() );
}
private String pathFrom(List<String> contexts, int index) {
return IntStream.range(0, contexts.size())
.filter( i -> i >= index )
.mapToObj( i -> contexts.get(i) )
.reduce( "", (s1,s2) -> s1 + "/" + s2 );
}
}