001/**
002 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
003 *   This file is part of the LDP4j Project:
004 *     http://www.ldp4j.org/
005 *
006 *   Center for Open Middleware
007 *     http://www.centeropenmiddleware.com/
008 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
009 *   Copyright (C) 2014 Center for Open Middleware.
010 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
011 *   Licensed under the Apache License, Version 2.0 (the "License");
012 *   you may not use this file except in compliance with the License.
013 *   You may obtain a copy of the License at
014 *
015 *             http://www.apache.org/licenses/LICENSE-2.0
016 *
017 *   Unless required by applicable law or agreed to in writing, software
018 *   distributed under the License is distributed on an "AS IS" BASIS,
019 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
020 *   See the License for the specific language governing permissions and
021 *   limitations under the License.
022 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
023 *   Artifact    : org.ldp4j.framework:ldp4j-application-api:0.2.0
024 *   Bundle      : ldp4j-application-api-0.2.0.jar
025 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
026 */
027package org.ldp4j.application.data;
028
029import java.io.Serializable;
030import java.net.URI;
031import java.util.HashMap;
032import java.util.Map;
033
034import javax.xml.namespace.QName;
035
036import org.ldp4j.application.vocabulary.Term;
037
038import com.google.common.collect.ImmutableMap;
039
040public final class NamingScheme {
041
042  public enum NameType {
043    TERM(Term.class),
044    URI(URI.class),
045    QNAME(QName.class),
046    STRING(String.class),
047    NUMBER(Number.class),
048    SERIALIZABLE(Serializable.class),
049    ;
050
051    private final Class<?> clazz;
052
053    private NameType(Class<?> clazz) {
054      this.clazz = clazz;
055    }
056
057    private static NameType valueOf(Object t) {
058      for(NameType candidate:values()) {
059        if(candidate.clazz.isInstance(t)) {
060          return candidate;
061        }
062      }
063      throw new IllegalArgumentException("Invalid name type '"+t.getClass().getCanonicalName()+"'");
064    }
065
066    @Override
067    public String toString() {
068      return clazz.getCanonicalName();
069    }
070
071  }
072
073  private enum NamingStrategy {
074    LOCAL,
075    GLOBAL,
076    ;
077  }
078
079  private interface NameFactory {
080
081    <T extends Serializable> Name<T> create(T id);
082
083  }
084
085  private static final class GlobalNameFactory implements NameFactory {
086
087    @Override
088    public <T extends Serializable> Name<T> create(T id) {
089      return ImmutableName.newGlobalName(id);
090    }
091
092  }
093
094  private static final class LocalNameFactory implements NameFactory {
095
096    @Override
097    public <T extends Serializable> Name<T> create(T id) {
098      return ImmutableName.newLocalName(id);
099    }
100
101  }
102
103  public static final class NamingSchemeBuilder {
104
105    private Map<NameType, NamingStrategy> configuration=new HashMap<NameType, NamingStrategy>();
106
107    private void addMappings(NamingStrategy strategy, NameType type, NameType... rest) {
108      this.configuration.put(type, strategy);
109      for(NameType r:rest) {
110        this.configuration.put(r, strategy);
111      }
112    }
113
114    public NamingSchemeBuilder withLocal(NameType type, NameType... rest) {
115      addMappings(NamingStrategy.LOCAL, type, rest);
116      return this;
117    }
118
119    public NamingSchemeBuilder withGlobal(NameType type, NameType... rest) {
120      addMappings(NamingStrategy.GLOBAL, type, rest);
121      return this;
122    }
123
124    public NamingScheme build() {
125      return new NamingScheme(this.configuration);
126    }
127
128  }
129
130  private static final Map<NamingStrategy,NameFactory> FACTORIES=
131    ImmutableMap.<NamingStrategy, NameFactory>
132      builder().
133        put(NamingStrategy.GLOBAL, new GlobalNameFactory()).
134        put(NamingStrategy.LOCAL, new LocalNameFactory()).
135        build();
136
137  private final Map<NameType, NamingStrategy> configuration;
138
139  private NamingScheme(Map<NameType,NamingStrategy> configuration) {
140    this.configuration = configuration;
141  }
142
143  private NameFactory getFactory(NameType type) {
144    NamingStrategy namingStrategy = configuration.get(type);
145    if(namingStrategy==null) {
146      namingStrategy=NamingStrategy.GLOBAL;
147    }
148    return FACTORIES.get(namingStrategy);
149  }
150
151
152  private static void append(StringBuilder builder, String part) {
153    if (part != null && !part.isEmpty()) {
154      if (builder.length() > 0) {
155        builder.append('.');
156      }
157      builder.append(part);
158    }
159  }
160
161  private static String assemble(String name, String... names) {
162    StringBuilder builder=new StringBuilder();
163    append(builder,name);
164    if(names!=null) {
165      for(String s:names) {
166        append(builder,s);
167      }
168    }
169    return builder.toString();
170  }
171
172  private <T extends Serializable> Name<T> createName(T id) {
173    return getFactory(NameType.valueOf(id)).create(id);
174  }
175
176  public Name<URI> name(URI id) {
177    return createName(id);
178  }
179
180  public Name<QName> name(QName id) {
181    return createName(id);
182  }
183
184  public Name<Term> name(Term id) {
185    return createName(id);
186  }
187
188  /**
189   * Concatenates elements to form a dotted name, discarding null values
190   * and empty strings.
191   *
192   * @param name
193   *            the first element of the name
194   * @param names
195   *            the remaining elements of the name
196   * @return {@code name} and {@code names} concatenated by periods
197   */
198  public Name<String> name(String name, String... names) {
199    return createName(assemble(name, names));
200  }
201
202  /**
203   * Concatenates a canonical class name and elements to form a dotted name, discarding any null values or empty strings
204   * any null values or empty strings.
205   *
206   * @param clazz
207   *            the first element of the name
208   * @param names
209   *            the remaining elements of the name
210   * @return {@code clazz} and {@code names} concatenated by periods
211   */
212  public Name<String> name(Class<?> clazz, String... names) {
213    return name(clazz.getCanonicalName(), names);
214  }
215
216  public <T extends Number> Name<T> name(T id) {
217    return createName(id);
218  }
219
220  public <T extends Serializable> Name<T> name(T id) {
221    return createName(id);
222  }
223
224  public static NamingSchemeBuilder builder() {
225    return new NamingSchemeBuilder();
226  }
227
228  public static NamingScheme getDefault() {
229    return builder().build();
230  }
231}