/* Copyright 2014 the original author or authors.
*
* 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.grails.encoder.impl;
import grails.util.GrailsNameUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.grails.encoder.ChainedDecoder;
import org.grails.encoder.ChainedEncoder;
import org.grails.encoder.ChainedEncoders;
import org.grails.encoder.CodecFactory;
import org.grails.encoder.CodecIdentifierProvider;
import org.grails.encoder.CodecLookup;
import org.grails.encoder.Decoder;
import org.grails.encoder.Encoder;
import org.grails.encoder.StreamingEncoder;
import org.springframework.beans.factory.InitializingBean;
public class BasicCodecLookup implements CodecLookup, InitializingBean {
private static final String NONE_CODEC_NAME = "none";
protected ConcurrentMap<String, Encoder> encoders;
protected ConcurrentMap<String, Decoder> decoders;
public static final StreamingEncoder NONE_ENCODER = new NoneEncoder();
public BasicCodecLookup() {
super();
}
public Encoder lookupEncoder(String codecName) {
return lookupCodec(codecName, encoders, Encoder.class);
}
public Decoder lookupDecoder(String codecName) {
return lookupCodec(codecName, decoders, Decoder.class);
}
@SuppressWarnings("unchecked")
protected <T extends CodecIdentifierProvider> T lookupCodec(String codecName, ConcurrentMap<String, T> map, Class<T> returnType) {
if (codecName != null && codecName.length() > 0) {
if (NONE_CODEC_NAME.equalsIgnoreCase(codecName)) {
if (returnType == Encoder.class) {
return (T)NONE_ENCODER;
}
} else {
T resultObject = map.get(codecName);
if(resultObject == null) {
resultObject = createCodec(codecName, map, returnType);
}
return resultObject;
}
}
return null;
}
@SuppressWarnings("unchecked")
protected <T extends CodecIdentifierProvider> T createCodec(String codecName, ConcurrentMap<String, T> map, Class<T> returnType) {
if(codecName.indexOf(',') > -1) {
T createdInstance = createChainedCodecInstance(codecName, map, returnType);
if(createdInstance != null) {
createdInstance = putChainedCodecInstance(codecName, map, createdInstance);
}
return createdInstance;
}
return null;
}
protected <T extends CodecIdentifierProvider> T putChainedCodecInstance(String codecName,
ConcurrentMap<String, T> map, T createdInstance) {
T previousInstance = map.putIfAbsent(codecName, createdInstance);
if(previousInstance != null) {
return previousInstance;
} else {
return createdInstance;
}
}
protected <T extends CodecIdentifierProvider> T createChainedCodecInstance(String codecName, ConcurrentMap<String, T> map, Class<T> returnType) {
String[] codecs=codecName.split(",");
List<T> codecInstances = new ArrayList<T>(codecs.length);
for(int i=0;i < codecs.length;i++) {
T codecInstance = map.get(codecs[i]);
if(codecInstance != null) {
codecInstances.add(codecInstance);
}
}
if (returnType == Encoder.class) {
List<StreamingEncoder> streamingEncoders = ChainedEncoders.toStreamingEncoders((List<Encoder>)codecInstances);
if(streamingEncoders == null) {
throw new RuntimeException("ChainedEncoder only supports StreamingEncoder instances. Couldn't build chained encoder for '" + codecName + "'");
} else {
return (T)ChainedEncoder.createFor(streamingEncoders);
}
} else {
Collections.reverse(codecInstances);
return (T)new ChainedDecoder(codecInstances.toArray(new Decoder[codecInstances.size()]));
}
}
protected synchronized <T extends CodecIdentifierProvider> void registerWithNameVaritions(Map<String, T> destinationMap, T target) {
String name=target.getCodecIdentifier().getCodecName();
registerVariationsOfName(destinationMap, target, name);
Set<String> aliases = target.getCodecIdentifier().getCodecAliases();
if (aliases != null) {
for (String alias : aliases) {
registerVariationsOfName(destinationMap, target, alias);
}
}
}
protected <T extends CodecIdentifierProvider> void registerVariationsOfName(Map<String, T> destinationMap, T target, String name) {
Collection<String> nameVariations = createNameVariations(name, target);
for(String nameVariation : nameVariations) {
destinationMap.put(nameVariation, target);
}
}
protected Collection<String> createNameVariations(String name, CodecIdentifierProvider target) {
Set<String> nameVariations = new LinkedHashSet<String>();
nameVariations.add(name);
nameVariations.add(name.toLowerCase());
nameVariations.add(name.toUpperCase());
nameVariations.add(GrailsNameUtils.getPropertyNameRepresentation(name));
return nameVariations;
}
public void registerCodecFactory(CodecFactory codecFactory) {
Encoder encoder=codecFactory.getEncoder();
if (encoder != null) {
registerEncoder(encoder);
}
Decoder decoder=codecFactory.getDecoder();
if (decoder != null) {
registerDecoder(decoder);
}
}
public void registerDecoder(Decoder decoder) {
registerWithNameVaritions(decoders, decoder);
}
public void registerEncoder(Encoder encoder) {
registerWithNameVaritions(encoders, encoder);
}
public void reInitialize() {
encoders = new ConcurrentHashMap<String, Encoder>();
decoders = new ConcurrentHashMap<String, Decoder>();
registerCodecs();
}
protected void registerCodecs() {
}
public void afterPropertiesSet() throws Exception {
reInitialize();
}
}