| /* |
| * 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 ); |
| |
| } |
| } |