Skip to content

Commit 1ae9079

Browse files
committed
[feature] Allow aliases to be configured for Internet Media Types.
This eables us to follow IETF RFC 7303 (e.g. 'text/xml' -> 'application/xml').
1 parent 47458e9 commit 1ae9079

18 files changed

Lines changed: 921 additions & 188 deletions

File tree

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright (C) 2014, Evolved Binary Ltd
3+
*
4+
* This file was originally ported from FusionDB to Elemental by
5+
* Evolved Binary, for the benefit of the Elemental Open Source community.
6+
* Only the ported code as it appears in this file, at the time that
7+
* it was contributed to Elemental, was re-licensed under The GNU
8+
* Lesser General Public License v2.1 only for use in Elemental.
9+
*
10+
* This license grant applies only to a snapshot of the code as it
11+
* appeared when ported, it does not offer or infer any rights to either
12+
* updates of this source code or access to the original source code.
13+
*
14+
* The GNU Lesser General Public License v2.1 only license follows.
15+
*
16+
* =====================================================================
17+
*
18+
* Elemental
19+
* Copyright (C) 2024, Evolved Binary Ltd
20+
*
21+
* admin@evolvedbinary.com
22+
* https://www.evolvedbinary.com | https://www.elemental.xyz
23+
*
24+
* This library is free software; you can redistribute it and/or
25+
* modify it under the terms of the GNU Lesser General Public
26+
* License as published by the Free Software Foundation; version 2.1.
27+
*
28+
* This library is distributed in the hope that it will be useful,
29+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
30+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
31+
* Lesser General Public License for more details.
32+
*
33+
* You should have received a copy of the GNU Lesser General Public
34+
* License along with this library; if not, write to the Free Software
35+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
36+
*/
37+
package xyz.elemental.mediatype;
38+
39+
/**
40+
* Information about a Media Type Alias. (aka MIME Type)
41+
*
42+
* @author <a href="mailto:adam@evolvedbinary.com">Adam Retter</a>
43+
*/
44+
public interface MediaTypeAlias extends MediaType {
45+
46+
/**
47+
* Get the alias identifier of the Media Type.
48+
*
49+
* For example {@code text/xml}.
50+
*
51+
* @return the identifier of the alias of the Media Type
52+
*/
53+
String getAliasIdentifier();
54+
}

elemental-media-type/elemental-media-type-impl/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@
8080
<artifactId>jcip-annotations</artifactId>
8181
</dependency>
8282

83+
<dependency>
84+
<groupId>com.evolvedbinary.j8fu</groupId>
85+
<artifactId>j8fu</artifactId>
86+
</dependency>
87+
8388
<dependency>
8489
<groupId>org.slf4j</groupId>
8590
<artifactId>slf4j-api</artifactId>
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/*
2+
* Copyright (C) 2014, Evolved Binary Ltd
3+
*
4+
* This file was originally ported from FusionDB to Elemental by
5+
* Evolved Binary, for the benefit of the Elemental Open Source community.
6+
* Only the ported code as it appears in this file, at the time that
7+
* it was contributed to Elemental, was re-licensed under The GNU
8+
* Lesser General Public License v2.1 only for use in Elemental.
9+
*
10+
* This license grant applies only to a snapshot of the code as it
11+
* appeared when ported, it does not offer or infer any rights to either
12+
* updates of this source code or access to the original source code.
13+
*
14+
* The GNU Lesser General Public License v2.1 only license follows.
15+
*
16+
* =====================================================================
17+
*
18+
* Elemental
19+
* Copyright (C) 2024, Evolved Binary Ltd
20+
*
21+
* admin@evolvedbinary.com
22+
* https://www.evolvedbinary.com | https://www.elemental.xyz
23+
*
24+
* This library is free software; you can redistribute it and/or
25+
* modify it under the terms of the GNU Lesser General Public
26+
* License as published by the Free Software Foundation; version 2.1.
27+
*
28+
* This library is distributed in the hope that it will be useful,
29+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
30+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
31+
* Lesser General Public License for more details.
32+
*
33+
* You should have received a copy of the GNU Lesser General Public
34+
* License along with this library; if not, write to the Free Software
35+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
36+
*/
37+
package xyz.elemental.mediatype.impl;
38+
39+
import net.jcip.annotations.NotThreadSafe;
40+
import xyz.elemental.mediatype.MediaType;
41+
import xyz.elemental.mediatype.MediaTypeAlias;
42+
import xyz.elemental.mediatype.StorageType;
43+
44+
import javax.annotation.Nullable;
45+
46+
/**
47+
* Immutable implementation of a Media Type Alias.
48+
*
49+
* @author <a href="mailto:adam@evolvedbinary.com">Adam Retter</a>
50+
*/
51+
public class MediaTypeAliasImpl implements MediaTypeAlias {
52+
53+
private final String aliasIdentifier;
54+
private final MediaType target;
55+
56+
private MediaTypeAliasImpl(final String aliasIdentifier, final MediaType target) {
57+
this.aliasIdentifier = aliasIdentifier;
58+
this.target = target;
59+
}
60+
61+
@Override
62+
public String getAliasIdentifier() {
63+
return aliasIdentifier;
64+
}
65+
66+
@Override
67+
public String getIdentifier() {
68+
return target.getIdentifier();
69+
}
70+
71+
@Nullable
72+
@Override
73+
public String[] getKnownFileExtensions() {
74+
return target.getKnownFileExtensions();
75+
}
76+
77+
@Override
78+
public StorageType getStorageType() {
79+
return target.getStorageType();
80+
}
81+
82+
/**
83+
* Construct a new Media Type Alias.
84+
*
85+
* @param aliasIdentifier the Media Type alias identifier
86+
*
87+
* @return a media type alias builder.
88+
*/
89+
public static MediaTypeAliasImpl.Builder builder(final String aliasIdentifier) {
90+
return MediaTypeAliasImpl.Builder.alias(aliasIdentifier);
91+
}
92+
93+
/**
94+
* Builder pattern which allows us to
95+
* ultimately construct an Immutable MediaTypeImpl.
96+
*/
97+
@NotThreadSafe
98+
public static class Builder {
99+
private final String aliasIdentifier;
100+
private @Nullable MediaType target;
101+
102+
private Builder(final String aliasIdentifier) {
103+
this.aliasIdentifier = aliasIdentifier;
104+
}
105+
106+
/**
107+
* Initiate the build of a MediaTypeImpl.
108+
*
109+
* @param aliasIdentifier the Media Type alias identifier
110+
* @return a Media Type Alias builder
111+
*/
112+
static MediaTypeAliasImpl.Builder alias(final String aliasIdentifier) {
113+
return new MediaTypeAliasImpl.Builder(aliasIdentifier);
114+
}
115+
116+
/**
117+
* Set the target for the Media Type alias.
118+
*
119+
* @param target a media type.
120+
* @return this
121+
*/
122+
public MediaTypeAliasImpl.Builder of(final MediaType target) {
123+
this.target = target;
124+
return this;
125+
}
126+
127+
/**
128+
* Build the Immutable MediaTypeAlias.
129+
*
130+
* @return an immutable MediaTypeAlias.
131+
*/
132+
public MediaTypeAlias build() {
133+
if (target == null) {
134+
throw new IllegalStateException("A target must be set for the alias");
135+
}
136+
137+
return new MediaTypeAliasImpl(aliasIdentifier, target);
138+
}
139+
}
140+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/*
2+
* Copyright (C) 2014, Evolved Binary Ltd
3+
*
4+
* This file was originally ported from FusionDB to Elemental by
5+
* Evolved Binary, for the benefit of the Elemental Open Source community.
6+
* Only the ported code as it appears in this file, at the time that
7+
* it was contributed to Elemental, was re-licensed under The GNU
8+
* Lesser General Public License v2.1 only for use in Elemental.
9+
*
10+
* This license grant applies only to a snapshot of the code as it
11+
* appeared when ported, it does not offer or infer any rights to either
12+
* updates of this source code or access to the original source code.
13+
*
14+
* The GNU Lesser General Public License v2.1 only license follows.
15+
*
16+
* =====================================================================
17+
*
18+
* Elemental
19+
* Copyright (C) 2024, Evolved Binary Ltd
20+
*
21+
* admin@evolvedbinary.com
22+
* https://www.evolvedbinary.com | https://www.elemental.xyz
23+
*
24+
* This library is free software; you can redistribute it and/or
25+
* modify it under the terms of the GNU Lesser General Public
26+
* License as published by the Free Software Foundation; version 2.1.
27+
*
28+
* This library is distributed in the hope that it will be useful,
29+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
30+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
31+
* Lesser General Public License for more details.
32+
*
33+
* You should have received a copy of the GNU Lesser General Public
34+
* License along with this library; if not, write to the Free Software
35+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
36+
*/
37+
package xyz.elemental.mediatype.impl;
38+
39+
import com.evolvedbinary.j8fu.tuple.Tuple2;
40+
import io.lacuna.bifurcan.IList;
41+
import io.lacuna.bifurcan.IMap;
42+
import io.lacuna.bifurcan.LinearMap;
43+
import net.jcip.annotations.NotThreadSafe;
44+
import org.slf4j.Logger;
45+
import org.slf4j.LoggerFactory;
46+
import xyz.elemental.mediatype.impl.MediaTypeConfigUtil.MediaTypeConfigSource;
47+
import xyz.elemental.mediatype.impl.configuration.MediaTypeAlias;
48+
import xyz.elemental.mediatype.impl.configuration.MediaTypeAliases;
49+
50+
import javax.annotation.Nullable;
51+
import java.nio.file.Path;
52+
53+
import static com.evolvedbinary.j8fu.tuple.Tuple.Tuple;
54+
import static xyz.elemental.mediatype.impl.MediaTypeConfigUtil.findConfigSources;
55+
import static xyz.elemental.mediatype.impl.MediaTypeConfigUtil.parseConfigSources;
56+
57+
/**
58+
* Maps Media Type Aliases to Media Types.
59+
* <h2>Aliases file search order.</h2><p>
60+
* The MediaTypeAliaser looks in various places in the user's
61+
* system for media type aliases files. When requests are made
62+
* to resolve media type aliases to media types, it searches
63+
* aliases files in the following order:
64+
* <ol>
65+
* <li>The file <code>media-type-aliases.xml</code> from within the user's home directory:
66+
* <ul>
67+
* <li>Linux/Unix: $XDG_CONFIG_HOME/elemental/media-type-aliases.xml. If $XDG_CONFIG_HOME is not set then, ~/.config/elemental/media-type-aliases.xml</li>
68+
* <li>macOS: $XDG_CONFIG_HOME/elemental/media-type-aliases.xml. If $XDG_CONFIG_HOME is not set then, ~/Library/Preferences/xyz.elemental/media-type-aliases.xml</li>
69+
* <li>Windows: %APPDATA%/Elemental/media-type-aliases.xml. If %APPDATA% is not set then, %USERPROFILE%/AppData/Local/Elemental/media-type-aliases.xml</li>
70+
* </ul>
71+
* </li>
72+
* <li>One or more files named <code>media-type-aliases.xml</code> in the Application's config directory(s).</li>
73+
* <li>The file <code>media-type-aliases.xml</code> on the classpath in the package xyz.elemental.mediatype.</li>
74+
* </ol>
75+
*
76+
* @author <a href="mailto:adam@evolvedbinary.com">Adam Retter</a>
77+
*/
78+
@NotThreadSafe
79+
public class MediaTypeAliaser {
80+
81+
private static final Logger LOG = LoggerFactory.getLogger(MediaTypeAliaser.class);
82+
83+
private static final String MEDIA_TYPE_ALIASES_FILENAME = "media-type-aliases.xml";
84+
85+
final IMap<String, String> aliases;
86+
87+
public MediaTypeAliaser(@Nullable final Path... configDirs) {
88+
final IList<MediaTypeConfigSource> aliasesFileSources = findConfigSources(MediaTypeAliaser.class, MEDIA_TYPE_ALIASES_FILENAME, configDirs);
89+
final IList<Tuple2<String, String>> aliases = parseConfigSources(MediaTypeAliases.class, aliasesFileSources, MediaTypeAliaser::parseAliases);
90+
this.aliases = toMap(aliases);
91+
}
92+
93+
private static void parseAliases(final String configLocation, final MediaTypeAliases mediaTypeAliases, final IList<Tuple2<String, String>> aliases) {
94+
if (mediaTypeAliases.getMediaTypeAlias() == null) {
95+
LOG.error("No aliases found in {} skipping...", configLocation);
96+
return;
97+
}
98+
99+
for (final MediaTypeAlias alias : mediaTypeAliases.getMediaTypeAlias()) {
100+
aliases.addLast(Tuple(alias.getAlias(), alias.getTarget()));
101+
}
102+
}
103+
104+
private static IMap<String, String> toMap(final IList<Tuple2<String, String>> list) {
105+
final IMap<String, String> map = new LinearMap<>((int) list.size());
106+
for (final Tuple2<String, String> entry : list) {
107+
map.put(entry._1, entry._2);
108+
}
109+
return map.forked();
110+
}
111+
112+
/**
113+
* Given a Media Type alias identifier try and resolve its Media Type identifier.
114+
*
115+
* @param mediaTypeAliasIdentifier the identifier of the Media Type alias.
116+
*
117+
* @return aliased Media Type Identifier, or null if there is no such alias.
118+
*/
119+
public @Nullable String resolveAlias(final String mediaTypeAliasIdentifier) {
120+
return resolveAlias(mediaTypeAliasIdentifier, null);
121+
}
122+
123+
/**
124+
* Given a Media Type alias identifier try and resolve its Media Type identifier.
125+
*
126+
* @param mediaTypeAliasIdentifier the identifier of the Media Type alias.
127+
* @param defaultMediaTypeIdentifier the default Media Type identifier to return
128+
* if a Media Type identifier cannot be resolved for the Media Type alias identifier.
129+
*
130+
* @return aliased Media Type Identifier, or {@code defaultMediaTypeIdentifier} if there is no such alias.
131+
*/
132+
public @Nullable String resolveAlias(final String mediaTypeAliasIdentifier, @Nullable final String defaultMediaTypeIdentifier) {
133+
return aliases.get(mediaTypeAliasIdentifier, defaultMediaTypeIdentifier);
134+
}
135+
}

0 commit comments

Comments
 (0)