[LIB-4] Add hespiff source files
authorgotty <gotty@fb0bcced-7025-49ed-a12f-f98bce993226>
Fri, 1 Jul 2016 22:31:57 +0000 (22:31 +0000)
committergotty <gotty@fb0bcced-7025-49ed-a12f-f98bce993226>
Fri, 1 Jul 2016 22:31:57 +0000 (22:31 +0000)
git-svn-id: https://svn.hedgecode.org/xml/hespiff/trunk@69 fb0bcced-7025-49ed-a12f-f98bce993226

43 files changed:
pom.xml [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/AbstractXMLBindElement.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/Attribution.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/Element.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/Extension.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/Link.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/Meta.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/Playlist.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/PlaylistFactory.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/Properties.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/Track.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/XMLBindPlaylist.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/XMLBindTrack.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/XSPF.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/XSPFAttribution.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/XSPFConstants.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/XSPFException.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/XSPFExtension.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/XSPFLink.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/XSPFMeta.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/XSPFPlaylist.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/XSPFTrack.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/bind/AbstractRNGBinder.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/bind/AbstractXSDBinder.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/bind/Binder.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/bind/BinderFactory.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/bind/PlaylistBinder.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/bind/RNGPlaylistBinder.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/bind/RNGTrackBinder.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/bind/TrackBinder.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/bind/XSDPlaylistBinder.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/bind/XSDTrackBinder.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/validate/EmptyValidator.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/validate/URIValidator.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/validate/Validate.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/validate/ValidateException.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/validate/Validator.java [new file with mode: 0644]
src/main/java/org/hedgecode/xml/xspf/validate/ValidatorType.java [new file with mode: 0644]
src/main/rng/xspf-1_0.7.rng [new file with mode: 0644]
src/main/xsd/xspf-1_0.2.xsd [new file with mode: 0644]
src/test/java/org/hedgecode/xml/xspf/XSPFTest.java [new file with mode: 0644]
src/test/resources/playlist.xspf [new file with mode: 0644]
src/test/resources/torrent-tv.ru.xspf [new file with mode: 0644]

diff --git a/pom.xml b/pom.xml
new file mode 100644 (file)
index 0000000..c648b54
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,214 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<!--
+  ~ Copyright (c) 2015. Developed by Hedgecode.
+  ~
+  ~ 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.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <!--parent>
+        <groupId>org.hedgecode.maven</groupId>
+        <artifactId>maven-parent</artifactId>
+        <version>1</version>
+    </parent-->
+
+    <groupId>org.hedgecode.xml</groupId>
+    <artifactId>hespiff</artifactId>
+    <version>0.1-SNAPSHOT</version>
+    <packaging>jar</packaging>
+
+    <name>Hedgecode XSPF API</name>
+    <description>
+        Hedgecode XSPF API.
+    </description>
+    <inceptionYear>2015</inceptionYear>
+
+    <url>http://hedgecode.org/xml/${project.artifactId}/</url>
+
+    <scm>
+        <connection>scm:svn:http://svn.hedgecode.org/xml/${project.artifactId}/trunk/</connection>
+        <developerConnection>scm:svn:http://svn.hedgecode.org/xml/${project.artifactId}/trunk/</developerConnection>
+        <url>http://svn.hedgecode.org/xml/${project.artifactId}/trunk/</url>
+    </scm>
+
+    <properties>
+        <javaVersion>7</javaVersion>
+        <maven.compiler.source>1.${javaVersion}</maven.compiler.source>
+        <maven.compiler.target>1.${javaVersion}</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <junitVersion>4.8.2</junitVersion>
+
+        <project.package>${project.groupId}.xspf</project.package>
+        <xsd.directory>${project.basedir}/src/main/xsd</xsd.directory>
+        <xsd.filename>xspf-1_0.2</xsd.filename>
+        <relaxng.directory>${project.basedir}/src/main/rng</relaxng.directory>
+        <relaxng.filename>xspf-1_0.7</relaxng.filename>
+        <generated.xsd.directory>${project.build.directory}/generated-xsd</generated.xsd.directory>
+        <generated.sources.directory>${project.build.directory}/generated-sources</generated.sources.directory>
+        <generated.meta.directory>${project.build.outputDirectory}/META-INF</generated.meta.directory>
+
+        <!--mavenDependencyPluginVersion>2.8</mavenDependencyPluginVersion>
+        <mavenJarPluginVersion>2.5</mavenJarPluginVersion>
+        <mavenAssemblyPluginVersion>2.4.1</mavenAssemblyPluginVersion>
+        <mavenReleasePluginVersion>2.5.3</mavenReleasePluginVersion-->
+
+        <issueKeyNumber>LIB-4</issueKeyNumber>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>${junitVersion}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!--plugin>
+              <groupId>org.apache.maven.plugins</groupId>
+              <artifactId>maven-resources-plugin</artifactId>
+              <version>2.7</version>
+              <executions>
+                  <execution>
+                      <id>copy-xsd</id>
+                      <phase>validate</phase>
+                      <goals>
+                          <goal>copy-resources</goal>
+                      </goals>
+                      <configuration>
+                          <outputDirectory>${generated.xsd.directory}</outputDirectory>
+                          <resources>          
+                              <resource>
+                                  <directory>${xsd.directory}</directory>
+                              </resource>
+                          </resources>              
+                      </configuration>            
+                  </execution>
+              </executions>
+            </plugin-->
+            <plugin>
+              <groupId>org.apache.maven.plugins</groupId>
+              <artifactId>maven-resources-plugin</artifactId>
+              <version>2.7</version>
+              <executions>
+                  <execution>
+                      <id>copy-xsd</id>
+                      <phase>validate</phase>
+                      <goals>
+                          <goal>copy-resources</goal>
+                      </goals>
+                      <configuration>
+                          <outputDirectory>${generated.meta.directory}</outputDirectory>
+                          <resources>
+                              <resource>
+                                  <directory>${xsd.directory}</directory>
+                              </resource>
+                              <resource>
+                                  <directory>${relaxng.directory}</directory>
+                              </resource>
+                          </resources>
+                      </configuration>
+                  </execution>
+              </executions>
+            </plugin>
+            <plugin>
+                <groupId>us.bryon</groupId>
+                <artifactId>relaxng-maven-plugin</artifactId>
+                <version>1.1</version>
+                <executions>
+                    <execution>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>trang</goal>
+                        </goals>
+                        <configuration>
+                            <translations>
+                                <translation>
+                                    <in>${relaxng.directory}/${relaxng.filename}.rng</in>
+                                    <out>${generated.xsd.directory}/${relaxng.filename}.xsd</out>
+                                </translation>
+                            </translations>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>jaxb2-maven-plugin</artifactId>
+                <version>2.2</version>
+                <executions>
+                    <execution>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>xjc</goal>
+                        </goals>
+                        <configuration>
+                            <outputDirectory>${generated.sources.directory}/xsd</outputDirectory>
+                            <packageName>${project.package}.xsd</packageName>
+                            <arguments>
+                                <argument>-episode</argument>
+                                <argument>${generated.meta.directory}/xsd-jaxb.episode</argument>
+                            </arguments>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.jvnet.jaxb2.maven2</groupId>
+                <artifactId>maven-jaxb2-plugin</artifactId>
+                <version>0.13.1</version>
+                <executions>
+                    <execution>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>generate</goal>
+                        </goals>
+                        <configuration>
+                            <schemaDirectory>${generated.xsd.directory}</schemaDirectory>
+                            <generateDirectory>${generated.sources.directory}/rng</generateDirectory>
+                            <generatePackage>${project.package}.rng</generatePackage>
+                            <episodeFile>${generated.meta.directory}/rng-jaxb.episode</episodeFile>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <version>1.9.1</version>
+                <executions>
+                    <execution>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>add-source</goal>
+                        </goals>
+                        <configuration>
+                            <sources>
+                                <source>${generated.sources.directory}/xsd</source>
+                                <source>${generated.sources.directory}/rng</source>
+                            </sources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/src/main/java/org/hedgecode/xml/xspf/AbstractXMLBindElement.java b/src/main/java/org/hedgecode/xml/xspf/AbstractXMLBindElement.java
new file mode 100644 (file)
index 0000000..059f25a
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf;
+
+import java.util.List;
+
+import org.hedgecode.xml.xspf.bind.Binder;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public abstract class AbstractXMLBindElement implements Element {
+
+    protected abstract Binder getBinder();
+
+    @Override
+    public String getTitle() {
+        return getBinder().getTitle();
+    }
+
+    @Override
+    public void setTitle(String title) {
+        getBinder().setTitle(title);
+    }
+
+    @Override
+    public String getCreator() {
+        return getBinder().getCreator();
+    }
+
+    @Override
+    public void setCreator(String creator) {
+        getBinder().setCreator(creator);
+    }
+
+    @Override
+    public String getAnnotation() {
+        return getBinder().getAnnotation();
+    }
+
+    @Override
+    public void setAnnotation(String annotation) {
+        getBinder().setAnnotation(annotation);
+    }
+
+    @Override
+    public String getInfo() {
+        return getBinder().getInfo();
+    }
+
+    @Override
+    public void setInfo(String info) {
+        getBinder().setInfo(info);
+    }
+
+    @Override
+    public String getImage() {
+        return getBinder().getImage();
+    }
+
+    @Override
+    public void setImage(String image) {
+        getBinder().setImage(image);
+    }
+
+    @Override
+    public List<Link> getLinks() {
+        return getBinder().getLinks();
+    }
+
+    @Override
+    public void addLink(Link link) {
+        getBinder().addLink(link);
+    }
+
+    @Override
+    public List<Meta> getMetas() {
+        return getBinder().getMetas();
+    }
+
+    @Override
+    public void addMeta(Meta meta) {
+        getBinder().addMeta(meta);
+    }
+
+    @Override
+    public List<Extension> getExtensions() {
+        return getBinder().getExtensions();
+    }
+
+    @Override
+    public void addExtension(Extension extension) {
+        getBinder().addExtension(extension);
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/Attribution.java b/src/main/java/org/hedgecode/xml/xspf/Attribution.java
new file mode 100644 (file)
index 0000000..0c679de
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf;
+
+import java.util.Map;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface Attribution {
+
+    Map<String, String> get();
+
+    void add(String identifier, String location);
+
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/Element.java b/src/main/java/org/hedgecode/xml/xspf/Element.java
new file mode 100644 (file)
index 0000000..cd6f557
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf;
+
+import java.util.List;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface Element {
+
+    String getTitle();
+
+    void setTitle(String title);
+
+    String getCreator();
+
+    void setCreator(String creator);
+
+    String getAnnotation();
+
+    void setAnnotation(String annotation);
+
+    String getInfo();
+
+    void setInfo(String info);
+
+    String getImage();
+
+    void setImage(String image);
+
+    List<Link> getLinks();
+
+    void addLink(Link link);
+
+    List<Meta> getMetas();
+
+    void addMeta(Meta meta);
+
+    List<Extension> getExtensions();
+
+    void addExtension(Extension extension);
+
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/Extension.java b/src/main/java/org/hedgecode/xml/xspf/Extension.java
new file mode 100644 (file)
index 0000000..ebcd9de
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf;
+
+import java.util.List;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface Extension {
+
+    String getApplication();
+
+    List<Object> getContent();
+
+    void set(String application, List<Object> content);
+
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/Link.java b/src/main/java/org/hedgecode/xml/xspf/Link.java
new file mode 100644 (file)
index 0000000..8b3d3c4
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface Link {
+
+    String getRel();
+
+    String getContent();
+
+    void set(String rel, String content);
+
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/Meta.java b/src/main/java/org/hedgecode/xml/xspf/Meta.java
new file mode 100644 (file)
index 0000000..4784fb4
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface Meta {
+
+    String getRel();
+
+    String getContent();
+
+    void set(String rel, String content);
+
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/Playlist.java b/src/main/java/org/hedgecode/xml/xspf/Playlist.java
new file mode 100644 (file)
index 0000000..b9728e8
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf;
+
+import java.io.File;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Date;
+import java.util.List;
+
+import javax.xml.bind.JAXBException;
+
+import org.hedgecode.xml.xspf.validate.ValidateException;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface Playlist extends Element {
+
+    String getLocation();
+
+    void setLocation(String location);
+
+    String getIdentifier();
+
+    void setIdentifier(String identifier);
+
+    Date getDate();
+
+    void setDate(Date date);
+
+    String getLicense();
+
+    void setLicense(String license);
+
+    Attribution getAttribution();
+
+    void setAttribution(Attribution attribution);
+
+    List<Track> getTracks();
+
+    void addTrack(Track track);
+
+    void read(File inputFile) throws JAXBException;
+
+    void write(File outputFile) throws JAXBException;
+
+    public OutputStream getAsStream() throws JAXBException;
+
+    public String getAsString() throws JAXBException, UnsupportedEncodingException;
+
+    void validate() throws ValidateException;
+
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/PlaylistFactory.java b/src/main/java/org/hedgecode/xml/xspf/PlaylistFactory.java
new file mode 100644 (file)
index 0000000..1e74eb2
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf;
+
+import java.util.List;
+
+import org.hedgecode.xml.xspf.bind.TrackBinder;
+
+import static org.hedgecode.xml.xspf.XSPFConstants.Format;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class PlaylistFactory {
+
+    public static Playlist createPlaylist(Format format) {
+        return new XSPFPlaylist(format);
+    }
+
+    public static Track createTrack() {
+        return new XSPFTrack();
+    }
+
+    public static Track createTrack(TrackBinder trackBinder) {
+        XSPFTrack track = new XSPFTrack();
+        track.unbind(
+                new XMLBindTrack(trackBinder)
+        );
+        return track;
+    }
+
+    public static void bindTrack(Track track, TrackBinder trackBinder) {
+        if (track instanceof XSPFTrack)
+            ((XSPFTrack)track).bind(
+                    new XMLBindTrack(trackBinder)
+            );
+    }
+
+    public static Attribution createAttribution() {
+        Attribution attribution = new XSPFAttribution();
+        return attribution;
+    }
+
+    public static Attribution createAttribution(String identifier, String location) {
+        Attribution attribution = new XSPFAttribution();
+        attribution.add(identifier, location);
+        return attribution;
+    }
+
+    public static Link createLink(String rel, String content) {
+        return new XSPFLink(rel, content);
+    }
+
+    public static Meta createMeta(String rel, String content) {
+        return new XSPFMeta(rel, content);
+    }
+
+    public static Extension createExtension(String application, List<Object> content) {
+        return new XSPFExtension(application, content);
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/Properties.java b/src/main/java/org/hedgecode/xml/xspf/Properties.java
new file mode 100644 (file)
index 0000000..b8d0584
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class Properties {
+
+    public static final String CHARSET = "xspf.charset";
+    public static final String FORMATTED = "xspf.formatted";
+    public static final String STANDALONE = "xspf.standalone";
+
+    private static final Map<String, Object> props = new HashMap<String, Object>() {
+        {
+            put(CHARSET, XSPFConstants.DEF_CHARSET);
+            put(FORMATTED, Boolean.TRUE);
+            put(STANDALONE, Boolean.FALSE);
+        }
+    };
+
+    public static void setProperty(String name, Object value) {
+        if (props.containsKey(name)) {
+            if (props.get(name).getClass().equals(value.getClass()))
+                props.put(name, value);
+        }
+    }
+
+    public static Object getProperty(String name, Class<?> clazz) {
+        if (clazz != null && props.containsKey(name)) {
+            Object property = props.get(name);
+            if (clazz.isAssignableFrom(property.getClass()))
+                return property;
+        }
+        return null;
+    }
+
+    private Properties() {
+        throw new AssertionError(
+                "I am so paranoid!"
+        );
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/Track.java b/src/main/java/org/hedgecode/xml/xspf/Track.java
new file mode 100644 (file)
index 0000000..a591a6c
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf;
+
+import java.util.List;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface Track extends Element {
+
+    List<String> getLocations();
+
+    void addLocation(String location);
+
+    List<String> getIdentifiers();
+
+    void addIdentifier(String identifier);
+
+    String getAlbum();
+
+    void setAlbum(String album);
+
+    Integer getTrackNum();
+
+    void setTrackNum(Integer trackNum);
+
+    Integer getDuration();
+
+    void setDuration(Integer duration);
+
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/XMLBindPlaylist.java b/src/main/java/org/hedgecode/xml/xspf/XMLBindPlaylist.java
new file mode 100644 (file)
index 0000000..161e4f5
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.util.Date;
+import java.util.List;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.hedgecode.xml.xspf.bind.Binder;
+import org.hedgecode.xml.xspf.bind.BinderFactory;
+import org.hedgecode.xml.xspf.bind.PlaylistBinder;
+import org.hedgecode.xml.xspf.validate.ValidateException;
+
+import static org.hedgecode.xml.xspf.XSPFConstants.Format;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class XMLBindPlaylist extends AbstractXMLBindElement implements Playlist {
+
+    private PlaylistBinder binder;
+
+    protected XMLBindPlaylist(PlaylistBinder binder) {
+        if (binder == null)
+            this.binder = BinderFactory.createPlaylistBinder();
+        else
+            this.binder = binder;
+        setDefaults();
+    }
+
+    protected XMLBindPlaylist(Format format) {
+        this.binder = BinderFactory.createPlaylistBinder(format);
+        setDefaults();
+    }
+
+    private void setDefaults() {
+        binder.setVersion(XSPFConstants.XSPF_VERSION);
+    }
+
+    @Override
+    protected Binder getBinder() {
+        return binder;
+    }
+
+    @Override
+    public String getLocation() {
+        return binder.getLocation();
+    }
+
+    @Override
+    public void setLocation(String location) {
+        binder.setLocation(location);
+    }
+
+    @Override
+    public String getIdentifier() {
+        return binder.getIdentifier();
+    }
+
+    @Override
+    public void setIdentifier(String identifier) {
+        binder.setIdentifier(identifier);
+    }
+
+    @Override
+    public Date getDate() {
+        return binder.getDate();
+    }
+
+    @Override
+    public void setDate(Date date) {
+        binder.setDate(date);
+    }
+
+    @Override
+    public String getLicense() {
+        return binder.getLicense();
+    }
+
+    @Override
+    public void setLicense(String license) {
+        binder.setLicense(license);
+    }
+
+    @Override
+    public Attribution getAttribution() {
+        return binder.getAttribution();
+    }
+
+    @Override
+    public void setAttribution(Attribution attribution) {
+        binder.setAttribution(attribution);
+    }
+
+    @Override
+    public List<Track> getTracks() {
+        return binder.getTracks();
+    }
+
+    @Override
+    public void addTrack(Track track) {
+        binder.addTrack(track);
+    }
+
+    public void read(File inputFile) throws JAXBException {
+        binder.setPlaylist(
+                extractRootObject(
+                        binder.getPlaylistClass(),
+                        createUnmarshaller(
+                                binder.getPlaylistClass()
+                        ).unmarshal(inputFile)
+                )
+        );
+    }
+
+    @Override
+    public void write(File outputFile) throws JAXBException {
+        createMarshaller(
+                binder.getPlaylistClass()
+        ).marshal(
+                wrapPlaylist(binder),
+                outputFile
+        );
+    }
+
+    @Override
+    public OutputStream getAsStream() throws JAXBException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        createMarshaller(
+                binder.getPlaylistClass()
+        ).marshal(
+                wrapPlaylist(binder),
+                baos
+        );
+        return baos;
+    }
+
+    @Override
+    public String getAsString()
+            throws JAXBException, UnsupportedEncodingException
+    {
+        Charset charset = (Charset) Properties.getProperty(
+                Properties.CHARSET, Charset.class
+        );
+        ByteArrayOutputStream os = (ByteArrayOutputStream) getAsStream();
+        return os.toString(
+                charset.name()
+        );
+    }
+
+    private Marshaller createMarshaller(Class xmlRootClass) throws JAXBException {
+        JAXBContext jaxbContext = JAXBContext.newInstance(xmlRootClass);
+        Marshaller marshaller = jaxbContext.createMarshaller();
+        Charset charset = (Charset) Properties.getProperty(Properties.CHARSET, Charset.class);
+        marshaller.setProperty(
+                Marshaller.JAXB_ENCODING,
+                charset.name()
+        );
+        marshaller.setProperty(
+                Marshaller.JAXB_FORMATTED_OUTPUT,
+                Properties.getProperty(Properties.FORMATTED, Boolean.class)
+        );
+        Object standalone = Properties.getProperty(Properties.STANDALONE, Boolean.class);
+        if (Boolean.FALSE.equals(standalone)) {
+            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
+            marshaller.setProperty(
+                    XSPFConstants.XML_HEADER_NAME,
+                    XSPFConstants.XML_HEADER_VALUE.replace("%CHARSET%", charset.name())
+            );
+        }
+        return marshaller;
+    }
+
+    private Unmarshaller createUnmarshaller(Class xmlRootClass) throws JAXBException {
+        JAXBContext jaxbContext = JAXBContext.newInstance(xmlRootClass);
+        return jaxbContext.createUnmarshaller();
+    }
+
+    private Object extractRootObject(Class xmlRootClass, Object unmarshallingObject) {
+        if (xmlRootClass.getAnnotation(XmlRootElement.class) != null)
+            return unmarshallingObject;
+        return unmarshallingObject instanceof JAXBElement
+                ? ((JAXBElement) unmarshallingObject).getValue()
+                : null;
+
+    }
+
+    private Object wrapPlaylist(PlaylistBinder binder) {
+        if (binder.getPlaylistClass().getAnnotation(XmlRootElement.class) != null)
+            return binder.getPlaylist();
+        return binder.wrapPlaylist();
+    }
+
+    @Override
+    public void validate() throws ValidateException {
+
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/XMLBindTrack.java b/src/main/java/org/hedgecode/xml/xspf/XMLBindTrack.java
new file mode 100644 (file)
index 0000000..5ba70a5
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf;
+
+import java.util.List;
+
+import org.hedgecode.xml.xspf.bind.Binder;
+import org.hedgecode.xml.xspf.bind.BinderFactory;
+import org.hedgecode.xml.xspf.bind.TrackBinder;
+
+import static org.hedgecode.xml.xspf.XSPFConstants.Format;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class XMLBindTrack extends AbstractXMLBindElement implements Track {
+
+    private TrackBinder binder;
+
+    protected XMLBindTrack(TrackBinder binder) {
+        if (binder == null)
+            this.binder = BinderFactory.createTrackBinder();
+        else
+            this.binder = binder;
+    }
+
+    protected XMLBindTrack(Format format) {
+        this.binder = BinderFactory.createTrackBinder(format);
+    }
+
+    @Override
+    protected Binder getBinder() {
+        return binder;
+    }
+
+    @Override
+    public List<String> getLocations() {
+        return binder.getLocations();
+    }
+
+    @Override
+    public void addLocation(String location) {
+        binder.addLocation(location);
+    }
+
+    @Override
+    public List<String> getIdentifiers() {
+        return binder.getIdentifiers();
+    }
+
+    @Override
+    public void addIdentifier(String identifier) {
+        binder.addIdentifier(identifier);
+    }
+
+    @Override
+    public String getAlbum() {
+        return binder.getAlbum();
+    }
+
+    @Override
+    public void setAlbum(String album) {
+        binder.setAlbum(album);
+    }
+
+    @Override
+    public Integer getTrackNum() {
+        return binder.getTrackNum();
+    }
+
+    @Override
+    public void setTrackNum(Integer trackNum) {
+        binder.setTrackNum(trackNum);
+    }
+
+    @Override
+    public Integer getDuration() {
+        return binder.getDuration();
+    }
+
+    @Override
+    public void setDuration(Integer duration) {
+        binder.setDuration(duration);
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/XSPF.java b/src/main/java/org/hedgecode/xml/xspf/XSPF.java
new file mode 100644 (file)
index 0000000..845c57f
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf;
+
+import java.io.File;
+import java.io.UnsupportedEncodingException;
+
+import javax.xml.bind.JAXBException;
+
+import org.hedgecode.xml.xspf.validate.ValidateException;
+
+import static org.hedgecode.xml.xspf.XSPFConstants.Format;
+import static org.hedgecode.xml.xspf.XSPFConstants.Version;
+
+/**
+ * XSPF API for external applications.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class XSPF {
+
+    public static Playlist create() {
+        return create(Format.RELAX_NG);
+    }
+
+    public static Playlist create(Format format) {
+        return new XSPFPlaylist(format);
+    }
+
+    public static Playlist create(Version version) {
+        return new XSPFPlaylist(version);
+    }
+
+    public static Playlist create(String key) {
+        Format format = Format.byName(key);
+        if (format != null)
+            return create(format);
+
+        Version version = Version.byName(key);
+        if (version != null)
+            return create(version);
+
+        return null;
+    }
+
+    public static void generate(Playlist playlist, File outputFile) throws XSPFException {
+        if (playlist == null)
+            throw new XSPFException(XSPFConstants.Errors.NULL_PLAYLIST);
+        try {
+            playlist.write(outputFile);
+        } catch (JAXBException e) {
+            throw new XSPFException(XSPFConstants.Errors.BINDING_ERROR);
+        }
+    }
+
+    public static String generate(Playlist playlist) throws XSPFException {
+        if (playlist == null)
+            throw new XSPFException(XSPFConstants.Errors.NULL_PLAYLIST);
+        String result;
+        try {
+            result = playlist.getAsString();
+        } catch (JAXBException e) {
+            throw new XSPFException(XSPFConstants.Errors.BINDING_ERROR);
+        } catch (UnsupportedEncodingException e) {
+            throw new XSPFException(XSPFConstants.Errors.ENCODING_ERROR);
+        }
+        return result;
+    }
+
+    public static Playlist create(File inputFile) throws JAXBException {
+        Playlist playlist = PlaylistFactory.createPlaylist(Format.XSD);
+        playlist.read(inputFile);
+        return playlist;
+    }
+
+    public static Track createTrack(String location, String title) {
+        Track track = PlaylistFactory.createTrack();
+        track.addLocation(location);
+        track.setTitle(title);
+        return track;
+    }
+
+    public static Track addTrack(Playlist playlist, String location, String title) {
+        Track track = createTrack(location, title);
+        playlist.addTrack(track);
+        return track;
+    }
+
+    public static void addTrack(Playlist playlist, Track track) {
+        playlist.addTrack(track);
+    }
+
+    public static void addTracks(
+            Playlist playlist, Playlist tracksPlaylist) throws XSPFException
+    {
+        if (playlist == null)
+            throw new XSPFException(XSPFConstants.Errors.NULL_PLAYLIST);
+        if (tracksPlaylist != null) {
+            for (Track track : tracksPlaylist.getTracks()) {
+                playlist.addTrack(track);
+            }
+        }
+    }
+
+    public static void setProperty(String name, Object value) {
+        Properties.setProperty(name, value);
+    }
+
+    public static int validate(File inputFile) throws JAXBException {
+        int result = 0;
+        Playlist playlist = create(inputFile);
+        if (playlist != null)
+            try {
+                playlist.validate();
+            } catch (ValidateException e) {
+                result = e.getCode();
+            }
+        return result;
+    }
+
+    public static String get(int code) {
+        return null; // todo
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/XSPFAttribution.java b/src/main/java/org/hedgecode/xml/xspf/XSPFAttribution.java
new file mode 100644 (file)
index 0000000..fbf7302
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class XSPFAttribution implements Attribution {
+
+    private Map<String, String> idLocations;
+
+    public XSPFAttribution() {
+        idLocations = new HashMap<>();
+    }
+
+    public XSPFAttribution(Map<String, String> idLocations) {
+        this.idLocations = idLocations;
+    }
+
+    @Override
+    public Map<String, String> get() {
+        return idLocations;
+    }
+
+    @Override
+    public void add(String identifier, String location) {
+        idLocations.put(
+                identifier, location
+        );
+    }
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/XSPFConstants.java b/src/main/java/org/hedgecode/xml/xspf/XSPFConstants.java
new file mode 100644 (file)
index 0000000..158281b
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import javax.xml.namespace.QName;
+
+/**
+ * Store of library constants.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class XSPFConstants {
+
+    public static final Charset DEF_CHARSET = StandardCharsets.UTF_8;
+
+    public static final String XSD_FORMAT = "xsd";
+    public static final String RNG_FORMAT = "rng";
+
+    public static enum Format {
+
+        XSD      ( XSD_FORMAT ),
+        RELAX_NG ( RNG_FORMAT );
+
+        private String formatName;
+
+        Format(String name) {
+            this.formatName = name;
+        }
+
+        public String getName() {
+            return formatName;
+        }
+
+        public static Format byName(String formatName) {
+            for (Format format : Format.values()) {
+                if (format.getName().equals(formatName))
+                    return format;
+            }
+            return null;
+        }
+    }
+
+    public static final String VER_1_0_2 = "1_0.2";
+    public static final String VER_1_0_7 = "1_0.7";
+
+    public static enum Version {
+
+        V1_2 ( VER_1_0_2 ),
+        V1_7 ( VER_1_0_7 );
+
+        private String versionName;
+
+        Version(String name) {
+            this.versionName = name;
+        }
+
+        public String getName() {
+            return versionName;
+        }
+
+        public static Version byName(String versionName) {
+            for (Version version : Version.values()) {
+                if (version.getName().equals(versionName))
+                    return version;
+            }
+            return null;
+        }
+    }
+
+    public static final String XSPF_VERSION = "1";
+
+    public static final QName PLAYLIST_QNAME = new QName("http://xspf.org/ns/0/", "playlist");
+
+    public static final String XML_HEADER_NAME = "com.sun.xml.internal.bind.xmlHeaders";
+    public static final String XML_HEADER_VALUE = "<?xml version=\"1.0\" encoding=\"%CHARSET%\"?>";
+
+
+    public static interface Errors {
+        public static final int NULL_PLAYLIST = 101;
+        public static final int VALIDATE_ERROR = 201;
+        public static final int BINDING_ERROR = 301;
+        public static final int ENCODING_ERROR = 401;
+    }
+
+    private XSPFConstants() {
+        throw new AssertionError(
+                "No org.hedgecode.xml.xspf.XSPFConstants instances!"
+        );
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/XSPFException.java b/src/main/java/org/hedgecode/xml/xspf/XSPFException.java
new file mode 100644 (file)
index 0000000..67cb36c
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class XSPFException extends Exception {
+
+    private static final Map<Integer, String> errors = new HashMap<Integer, String>() {
+        {
+            put(XSPFConstants.Errors.NULL_PLAYLIST, "");
+            put(XSPFConstants.Errors.VALIDATE_ERROR, "");
+            put(XSPFConstants.Errors.BINDING_ERROR, "");
+            put(XSPFConstants.Errors.ENCODING_ERROR, "");
+        }
+    };
+
+    public XSPFException(int errorCode) {
+        super(errors.get(errorCode));
+    }
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/XSPFExtension.java b/src/main/java/org/hedgecode/xml/xspf/XSPFExtension.java
new file mode 100644 (file)
index 0000000..81e378a
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf;
+
+import java.util.List;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class XSPFExtension implements Extension {
+
+    private String application;
+    private List<Object> content;
+
+    public XSPFExtension() {
+    }
+
+    public XSPFExtension(String application, List<Object> content) {
+        this.application = application;
+        this.content = content;
+    }
+
+    @Override
+    public String getApplication() {
+        return application;
+    }
+
+    @Override
+    public List<Object> getContent() {
+        return content;
+    }
+
+    @Override
+    public void set(String application, List<Object> content) {
+        this.application = application;
+        this.content = content;
+    }
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/XSPFLink.java b/src/main/java/org/hedgecode/xml/xspf/XSPFLink.java
new file mode 100644 (file)
index 0000000..8420461
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class XSPFLink implements Link {
+
+    private String rel;
+    private String content;
+
+    public XSPFLink() {
+    }
+
+    public XSPFLink(String rel, String content) {
+        this.rel = rel;
+        this.content = content;
+    }
+
+    @Override
+    public String getRel() {
+        return rel;
+    }
+
+    @Override
+    public String getContent() {
+        return content;
+    }
+
+    @Override
+    public void set(String rel, String content) {
+        this.rel = rel;
+        this.content = content;
+    }
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/XSPFMeta.java b/src/main/java/org/hedgecode/xml/xspf/XSPFMeta.java
new file mode 100644 (file)
index 0000000..b756864
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class XSPFMeta implements Meta {
+
+    private String rel;
+    private String content;
+
+    public XSPFMeta() {
+    }
+
+    public XSPFMeta(String rel, String content) {
+        this.rel = rel;
+        this.content = content;
+    }
+
+    @Override
+    public String getRel() {
+        return rel;
+    }
+
+    @Override
+    public String getContent() {
+        return content;
+    }
+
+    @Override
+    public void set(String rel, String content) {
+        this.rel = rel;
+        this.content = content;
+    }
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/XSPFPlaylist.java b/src/main/java/org/hedgecode/xml/xspf/XSPFPlaylist.java
new file mode 100644 (file)
index 0000000..36736ba
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf;
+
+import java.io.File;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import javax.xml.bind.JAXBException;
+
+import org.hedgecode.xml.xspf.bind.BinderFactory;
+import org.hedgecode.xml.xspf.bind.PlaylistBinder;
+import org.hedgecode.xml.xspf.validate.Validate;
+import org.hedgecode.xml.xspf.validate.ValidateException;
+import org.hedgecode.xml.xspf.validate.Validator;
+import org.hedgecode.xml.xspf.validate.ValidatorType;
+
+import static org.hedgecode.xml.xspf.XSPFConstants.Format;
+import static org.hedgecode.xml.xspf.XSPFConstants.Version;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class XSPFPlaylist implements Playlist {
+
+    protected XSPFPlaylist(Format format) {
+        this.format = format;
+    }
+
+    protected XSPFPlaylist(Version version) {
+        this.format = Format.RELAX_NG;
+    }
+
+    private Format format;
+
+    /**
+     * Human-readable title for the playlist.
+     */
+    private String title;
+
+    /**
+     * Human-readable name of the entity (author, authors, group, company, etc)
+     * that authored the playlist.
+     */
+    private String creator;
+
+    /**
+     * Human-readable comment on the playlist.
+     * This is character data, not HTML, and it may not contain markup.
+     */
+    private String annotation;
+
+    /**
+     * URI of a web page to find out more about this playlist.
+     * Likely to be homepage of the author, and would be used to find out
+     * more about the author and to find more playlists by the author.
+     */
+    private String info;
+
+    /**
+     * Source URI for this playlist.
+     */
+    @Validate(validatorType = ValidatorType.URI)
+    private String location;
+
+    /**
+     * Canonical ID for this playlist.
+     * Likely to be a hash or other location-independent name.
+     * Must be a legal URI.
+     */
+    private String identifier;
+
+    /**
+     * URI of an image to display in the absence of
+     * a playlist->trackList->image element.
+     */
+    private String image;
+
+    /**
+     * Creation date (not last-modified date) of the playlist,
+     * formatted as a XML schema dateTime.
+     * In the absence of a timezone, the element may be assumed
+     * to use Coordinated Universal Time (UTC).
+     */
+    private Date date;
+
+    /**
+     * URI of a resource that describes the license
+     * under which this playlist was released.
+     */
+    private String license;
+
+    /**
+     * An ordered list of URIs. The purpose is to satisfy licenses
+     * allowing modification but requiring attribution.
+     * If you modify such a playlist, move its playlist->location
+     * or playlist->identifier element to the top of the items
+     * in the playlist->attribution element.
+     * Such a list can grow without limit, so as a practical matter
+     * we suggest deleting ancestors more than ten generations back.
+     */
+    private Attribution attribution;
+
+    /**
+     * The link element allows XSPF to be extended
+     * without the use of XML namespaces.
+     */
+    private List<Link> links = new ArrayList<>();
+
+    /**
+     * The meta element allows metadata fields to be added to XSPF.
+     */
+    private List<Meta> metas = new ArrayList<>();
+
+    /**
+     * The extension element allows non-XSPF XML to be included in XSPF documents.
+     * The purpose is to allow nested XML, which the meta and link elements do not.
+     */
+    private List<Extension> extensions = new ArrayList<>();
+
+    /**
+     * Ordered list of track elements to be rendered.
+     * The sequence is a hint, not a requirement;
+     * renderers are advised to play tracks from top to bottom
+     * unless there is an indication otherwise.
+     */
+    private List<Track> trackList = new ArrayList<>();
+
+
+    @Override
+    public String getTitle() {
+        return title;
+    }
+
+    @Override
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    @Override
+    public String getCreator() {
+        return creator;
+    }
+
+    @Override
+    public void setCreator(String creator) {
+        this.creator = creator;
+    }
+
+    @Override
+    public String getAnnotation() {
+        return annotation;
+    }
+
+    @Override
+    public void setAnnotation(String annotation) {
+        this.annotation = annotation;
+    }
+
+    @Override
+    public String getInfo() {
+        return info;
+    }
+
+    @Override
+    public void setInfo(String info) {
+        this.info = info;
+    }
+
+    @Override
+    public String getLocation() {
+        return location;
+    }
+
+    @Override
+    public void setLocation(String location) {
+        this.location = location;
+    }
+
+    @Override
+    public String getIdentifier() {
+        return identifier;
+    }
+
+    @Override
+    public void setIdentifier(String identifier) {
+        this.identifier = identifier;
+    }
+
+    @Override
+    public String getImage() {
+        return image;
+    }
+
+    @Override
+    public void setImage(String image) {
+        this.image = image;
+    }
+
+    @Override
+    public Date getDate() {
+        return date;
+    }
+
+    @Override
+    public void setDate(Date date) {
+        this.date = date;
+    }
+
+    @Override
+    public String getLicense() {
+        return license;
+    }
+
+    @Override
+    public void setLicense(String license) {
+        this.license = license;
+    }
+
+    @Override
+    public Attribution getAttribution() {
+        return attribution;
+    }
+
+    @Override
+    public void setAttribution(Attribution attribution) {
+        this.attribution = attribution;
+    }
+
+    @Override
+    public List<Link> getLinks() {
+        return links;
+    }
+
+    @Override
+    public void addLink(Link link) {
+        links.add(link);
+    }
+
+    @Override
+    public List<Meta> getMetas() {
+        return metas;
+    }
+
+    @Override
+    public void addMeta(Meta meta) {
+        metas.add(meta);
+    }
+
+    @Override
+    public List<Extension> getExtensions() {
+        return extensions;
+    }
+
+    @Override
+    public void addExtension(Extension extension) {
+        extensions.add(extension);
+    }
+
+    @Override
+    public List<Track> getTracks() {
+        return trackList;
+    }
+
+    @Override
+    public void addTrack(Track track) {
+        trackList.add(track);
+    }
+
+    private void bind(XMLBindPlaylist bindPlaylist) {
+        bindPlaylist.setTitle(title);
+        bindPlaylist.setCreator(creator);
+        bindPlaylist.setAnnotation(annotation);
+        bindPlaylist.setInfo(info);
+        bindPlaylist.setLocation(location);
+        bindPlaylist.setIdentifier(identifier);
+        bindPlaylist.setImage(image);
+        bindPlaylist.setDate(date);
+        bindPlaylist.setLicense(license);
+        bindPlaylist.setAttribution(attribution);
+        for (Link link : links)
+            bindPlaylist.addLink(link);
+        for (Meta meta :metas)
+            bindPlaylist.addMeta(meta);
+        for (Extension extension : extensions)
+            bindPlaylist.addExtension(extension);
+        for (Track track : trackList)
+            bindPlaylist.addTrack(track);
+    }
+
+    private void unbind(XMLBindPlaylist bindPlaylist) {
+        title = bindPlaylist.getTitle();
+        creator = bindPlaylist.getCreator();
+        annotation = bindPlaylist.getAnnotation();
+        info = bindPlaylist.getInfo();
+        location = bindPlaylist.getLocation();
+        identifier = bindPlaylist.getIdentifier();
+        image = bindPlaylist.getImage();
+        date = bindPlaylist.getDate();
+        license = bindPlaylist.getLicense();
+        attribution = bindPlaylist.getAttribution();
+        links = bindPlaylist.getLinks();
+        metas = bindPlaylist.getMetas();
+        extensions = bindPlaylist.getExtensions();
+        trackList = bindPlaylist.getTracks();
+    }
+
+    @Override
+    public void read(File inputFile) throws JAXBException {
+        XMLBindPlaylist bindPlaylist = new XMLBindPlaylist(format);
+        bindPlaylist.read(inputFile);
+        unbind(bindPlaylist);
+    }
+
+    @Override
+    public void write(File outputFile) throws JAXBException {
+        XMLBindPlaylist bindPlaylist = new XMLBindPlaylist(format);
+        bind(bindPlaylist);
+        bindPlaylist.write(outputFile);
+    }
+
+    @Override
+    public OutputStream getAsStream() throws JAXBException {
+        XMLBindPlaylist bindPlaylist = new XMLBindPlaylist(format);
+        bind(bindPlaylist);
+        return bindPlaylist.getAsStream();
+    }
+
+    @Override
+    public String getAsString()
+            throws JAXBException, UnsupportedEncodingException
+    {
+        XMLBindPlaylist bindPlaylist = new XMLBindPlaylist(format);
+        bind(bindPlaylist);
+        return bindPlaylist.getAsString();
+    }
+
+
+    @Override
+    public void validate() throws ValidateException {
+        for (Field field : getClass().getFields()) {
+            if (field.isAnnotationPresent(Validate.class)) {
+                Validator validator;
+                try {
+                    validator = field.getAnnotation(Validate.class).validatorType().getValidator();
+                    validator.validate(
+                            field.get(this)
+                    );
+                } catch (ReflectiveOperationException e) {
+                    throw new ValidateException(101, e.getMessage());
+                }
+            }
+        }
+    }
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/XSPFTrack.java b/src/main/java/org/hedgecode/xml/xspf/XSPFTrack.java
new file mode 100644 (file)
index 0000000..d844507
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class XSPFTrack implements Track {
+
+    protected XSPFTrack() {
+    }
+
+    /**
+     *
+     */
+    private List<String> locations = new ArrayList<>();
+
+    /**
+     *
+     */
+    private List<String> identifiers = new ArrayList<>();
+
+    /**
+     *
+     */
+    private String title;
+
+    /**
+     *
+     */
+    private String creator;
+
+    /**
+     *
+     */
+    private String annotation;
+
+    /**
+     *
+     */
+    private String info;
+
+    /**
+     *
+     */
+    private String image;
+
+    /**
+     *
+     */
+    private String album;
+
+    /**
+     *
+     */
+    private Integer trackNum;
+
+    /**
+     *
+     */
+    private Integer duration;
+
+    /**
+     *
+     */
+    private List<Link> links = new ArrayList<>();
+
+    /**
+     *
+     */
+    private List<Meta> metas = new ArrayList<>();
+
+    /**
+     *
+     */
+    private List<Extension> extensions = new ArrayList<>();
+
+    @Override
+    public List<String> getLocations() {
+        return locations;
+    }
+
+    @Override
+    public void addLocation(String location) {
+        locations.add(location);
+    }
+
+    @Override
+    public List<String> getIdentifiers() {
+        return identifiers;
+    }
+
+    @Override
+    public void addIdentifier(String identifier) {
+        identifiers.add(identifier);
+    }
+
+    @Override
+    public String getTitle() {
+        return title;
+    }
+
+    @Override
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    @Override
+    public String getCreator() {
+        return creator;
+    }
+
+    @Override
+    public void setCreator(String creator) {
+        this.creator = creator;
+    }
+
+    @Override
+    public String getAnnotation() {
+        return annotation;
+    }
+
+    @Override
+    public void setAnnotation(String annotation) {
+        this.annotation = annotation;
+    }
+
+    @Override
+    public String getInfo() {
+        return info;
+    }
+
+    @Override
+    public void setInfo(String info) {
+        this.info = info;
+    }
+
+    @Override
+    public String getImage() {
+        return image;
+    }
+
+    @Override
+    public void setImage(String image) {
+        this.image = image;
+    }
+
+    @Override
+    public String getAlbum() {
+        return album;
+    }
+
+    @Override
+    public void setAlbum(String album) {
+        this.album = album;
+    }
+
+    @Override
+    public Integer getTrackNum() {
+        return trackNum;
+    }
+
+    @Override
+    public void setTrackNum(Integer trackNum) {
+        this.trackNum = trackNum;
+    }
+
+    @Override
+    public Integer getDuration() {
+        return duration;
+    }
+
+    @Override
+    public void setDuration(Integer duration) {
+        this.duration = duration;
+    }
+
+    @Override
+    public List<Link> getLinks() {
+        return links;
+    }
+
+    @Override
+    public void addLink(Link link) {
+        links.add(link);
+    }
+
+    @Override
+    public List<Meta> getMetas() {
+        return metas;
+    }
+
+    @Override
+    public void addMeta(Meta meta) {
+        metas.add(meta);
+    }
+
+    @Override
+    public List<Extension> getExtensions() {
+        return extensions;
+    }
+
+    @Override
+    public void addExtension(Extension extension) {
+        extensions.add(extension);
+    }
+
+    protected void bind(XMLBindTrack bindTrack) {
+        for (String location : locations)
+            bindTrack.addLocation(location);
+        for (String identifier : identifiers)
+            bindTrack.addIdentifier(identifier);
+        bindTrack.setTitle(title);
+        bindTrack.setCreator(creator);
+        bindTrack.setAnnotation(annotation);
+        bindTrack.setInfo(info);
+        bindTrack.setImage(image);
+        bindTrack.setAlbum(album);
+        bindTrack.setTrackNum(trackNum);
+        bindTrack.setDuration(duration);
+        for (Link link : links)
+            bindTrack.addLink(link);
+        for (Meta meta :metas)
+            bindTrack.addMeta(meta);
+        for (Extension extension : extensions)
+            bindTrack.addExtension(extension);
+    }
+
+    protected void unbind(XMLBindTrack bindTrack) {
+        locations = bindTrack.getLocations();
+        identifiers = bindTrack.getIdentifiers();
+        title = bindTrack.getTitle();
+        creator = bindTrack.getCreator();
+        annotation = bindTrack.getAnnotation();
+        info = bindTrack.getInfo();
+        image = bindTrack.getImage();
+        album = bindTrack.getAlbum();
+        trackNum = bindTrack.getTrackNum();
+        duration = bindTrack.getDuration();
+        links = bindTrack.getLinks();
+        metas = bindTrack.getMetas();
+        extensions = bindTrack.getExtensions();
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/bind/AbstractRNGBinder.java b/src/main/java/org/hedgecode/xml/xspf/bind/AbstractRNGBinder.java
new file mode 100644 (file)
index 0000000..3ffa956
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf.bind;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.hedgecode.xml.xspf.PlaylistFactory;
+import org.hedgecode.xml.xspf.rng.Annotation;
+import org.hedgecode.xml.xspf.rng.Creator;
+import org.hedgecode.xml.xspf.rng.Extension;
+import org.hedgecode.xml.xspf.rng.Image;
+import org.hedgecode.xml.xspf.rng.Info;
+import org.hedgecode.xml.xspf.rng.Link;
+import org.hedgecode.xml.xspf.rng.Meta;
+import org.hedgecode.xml.xspf.rng.ObjectFactory;
+import org.hedgecode.xml.xspf.rng.Title;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public abstract class AbstractRNGBinder implements Binder {
+
+    protected static final ObjectFactory RNG_FACTORY = new ObjectFactory();
+
+    protected abstract Object getElement(Class elementClass);
+
+    protected abstract List<Object> getElements(Class elementClass);
+
+    protected abstract List<Object> getAllElements();
+
+    @Override
+    public String getTitle() {
+        Title titleElement = (Title) getElement(Title.class);
+        return titleElement != null ? titleElement.getContent() : null;
+    }
+
+    @Override
+    public void setTitle(String title) {
+        Title titleElement = (Title) getElement(Title.class);
+        if (titleElement != null)
+            getAllElements().remove(titleElement);
+        if (title != null) {
+            titleElement = RNG_FACTORY.createTitle();
+            titleElement.setContent(title);
+            getAllElements().add(titleElement);
+        }
+    }
+
+    @Override
+    public String getCreator() {
+        Creator creatorElement = (Creator) getElement(Creator.class);
+        return creatorElement != null ? creatorElement.getContent() : null;
+    }
+
+    @Override
+    public void setCreator(String creator) {
+        Creator creatorElement = (Creator) getElement(Creator.class);
+        if (creatorElement != null)
+            getAllElements().remove(creatorElement);
+        if (creator != null) {
+            creatorElement = RNG_FACTORY.createCreator();
+            creatorElement.setContent(creator);
+            getAllElements().add(creatorElement);
+        }
+    }
+
+    @Override
+    public String getAnnotation() {
+        Annotation annotationElement = (Annotation) getElement(Annotation.class);
+        return annotationElement != null ? annotationElement.getContent() : null;
+    }
+
+    @Override
+    public void setAnnotation(String annotation) {
+        Annotation annotationElement = (Annotation) getElement(Annotation.class);
+        if (annotationElement != null)
+            getAllElements().remove(annotationElement);
+        if (annotation != null) {
+            annotationElement = RNG_FACTORY.createAnnotation();
+            annotationElement.setContent(annotation);
+            getAllElements().add(annotationElement);
+        }
+    }
+
+    @Override
+    public String getInfo() {
+        Info infoElement = (Info) getElement(Info.class);
+        return infoElement != null ? infoElement.getValue() : null;
+    }
+
+    @Override
+    public void setInfo(String info) {
+        Info infoElement = (Info) getElement(Info.class);
+        if (infoElement != null)
+            getAllElements().remove(infoElement);
+        if (info != null) {
+            infoElement = RNG_FACTORY.createInfo();
+            infoElement.setValue(info);
+            getAllElements().add(infoElement);
+        }
+    }
+
+    @Override
+    public String getImage() {
+        Image imageElement = (Image) getElement(Image.class);
+        return imageElement != null ? imageElement.getValue() : null;
+    }
+
+    @Override
+    public void setImage(String image) {
+        Image imageElement = (Image) getElement(Image.class);
+        if (imageElement != null)
+            getAllElements().remove(imageElement);
+        if (image != null) {
+            imageElement = RNG_FACTORY.createImage();
+            imageElement.setValue(image);
+            getAllElements().add(imageElement);
+        }
+    }
+
+    @Override
+    public List<org.hedgecode.xml.xspf.Link> getLinks() {
+        List<org.hedgecode.xml.xspf.Link> links = new ArrayList<>();
+        for (Object element : getElements(Link.class)) {
+            Link linkElement = (Link) element;
+            links.add(
+                    PlaylistFactory.createLink(
+                            linkElement.getRel(), linkElement.getValue()
+                    )
+            );
+        }
+        return links;
+    }
+
+    @Override
+    public void addLink(org.hedgecode.xml.xspf.Link link) {
+        if (link != null) {
+            Link linkElement = RNG_FACTORY.createLink();
+            linkElement.setRel(link.getRel());
+            linkElement.setValue(link.getContent());
+            getAllElements().add(linkElement);
+        }
+    }
+
+    @Override
+    public List<org.hedgecode.xml.xspf.Meta> getMetas() {
+        List<org.hedgecode.xml.xspf.Meta> metas = new ArrayList<>();
+        for (Object element : getElements(Meta.class)) {
+            Meta metaElement = (Meta) element;
+            metas.add(
+                    PlaylistFactory.createMeta(
+                            metaElement.getRel(), metaElement.getContent()
+                    )
+            );
+        }
+        return metas;
+    }
+
+    @Override
+    public void addMeta(org.hedgecode.xml.xspf.Meta meta) {
+        if (meta != null) {
+            Meta metaElement = RNG_FACTORY.createMeta();
+            metaElement.setRel(meta.getRel());
+            metaElement.setContent(meta.getContent());
+            getAllElements().add(metaElement);
+        }
+    }
+
+    @Override
+    public List<org.hedgecode.xml.xspf.Extension> getExtensions() {
+        List<org.hedgecode.xml.xspf.Extension> extensions = new ArrayList<>();
+        for (Object element : getElements(Extension.class)) {
+            Extension extensionElement = (Extension) element;
+            extensions.add(
+                    PlaylistFactory.createExtension(
+                            extensionElement.getApplication(), extensionElement.getContent()
+                    )
+            );
+        }
+        return extensions;
+    }
+
+    @Override
+    public void addExtension(org.hedgecode.xml.xspf.Extension extension) {
+        if (extension != null) {
+            Extension extensionElement = RNG_FACTORY.createExtension();
+            extensionElement.setApplication(extension.getApplication());
+            extensionElement.getContent().addAll(extension.getContent());
+            getAllElements().add(extensionElement);
+        }
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/bind/AbstractXSDBinder.java b/src/main/java/org/hedgecode/xml/xspf/bind/AbstractXSDBinder.java
new file mode 100644 (file)
index 0000000..1e5fef8
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf.bind;
+
+import org.hedgecode.xml.xspf.xsd.ObjectFactory;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public abstract class AbstractXSDBinder implements Binder {
+
+    protected static final ObjectFactory XSD_FACTORY = new ObjectFactory();
+
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/bind/Binder.java b/src/main/java/org/hedgecode/xml/xspf/bind/Binder.java
new file mode 100644 (file)
index 0000000..c60c6fc
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf.bind;
+
+import java.util.List;
+
+import org.hedgecode.xml.xspf.Extension;
+import org.hedgecode.xml.xspf.Link;
+import org.hedgecode.xml.xspf.Meta;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface Binder {
+
+    String getTitle();
+
+    void setTitle(String title);
+
+    String getCreator();
+
+    void setCreator(String creator);
+
+    String getAnnotation();
+
+    void setAnnotation(String annotation);
+
+    String getInfo();
+
+    void setInfo(String info);
+
+    String getImage();
+
+    void setImage(String image);
+
+    List<Link> getLinks();
+
+    void addLink(Link link);
+
+    List<Meta> getMetas();
+
+    void addMeta(Meta meta);
+
+    List<Extension> getExtensions();
+
+    void addExtension(Extension extension);
+
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/bind/BinderFactory.java b/src/main/java/org/hedgecode/xml/xspf/bind/BinderFactory.java
new file mode 100644 (file)
index 0000000..a4cbe36
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf.bind;
+
+import org.hedgecode.xml.xspf.rng.Track;
+import org.hedgecode.xml.xspf.xsd.TrackType;
+
+import static org.hedgecode.xml.xspf.XSPFConstants.Format;
+
+/**
+ * Playlist/Track Binder Factory.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class BinderFactory {
+
+    public static PlaylistBinder createPlaylistBinder() {
+        return getDefaultPlaylistBinder();
+    }
+
+    public static PlaylistBinder createPlaylistBinder(Format format) {
+        switch(format) {
+            case RELAX_NG:
+                return new RNGPlaylistBinder();
+            case XSD:
+                return new XSDPlaylistBinder();
+            default:
+                return getDefaultPlaylistBinder();
+        }
+    }
+
+    public static TrackBinder createTrackBinder() {
+        return getDefaultTrackBinder();
+    }
+
+    public static TrackBinder createTrackBinder(Format format) {
+        switch(format) {
+            case RELAX_NG:
+                return new RNGTrackBinder();
+            case XSD:
+                return new XSDTrackBinder();
+            default:
+                return getDefaultTrackBinder();
+        }
+    }
+
+    public static TrackBinder createTrackBinder(Track track) {
+        return new RNGTrackBinder(track);
+    }
+
+    public static TrackBinder createTrackBinder(TrackType trackType) {
+        return new XSDTrackBinder(trackType);
+    }
+
+        private static PlaylistBinder getDefaultPlaylistBinder() {
+        return new RNGPlaylistBinder();
+    }
+
+    private static TrackBinder getDefaultTrackBinder() {
+        return new RNGTrackBinder();
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/bind/PlaylistBinder.java b/src/main/java/org/hedgecode/xml/xspf/bind/PlaylistBinder.java
new file mode 100644 (file)
index 0000000..ec270c5
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf.bind;
+
+import java.util.Date;
+import java.util.List;
+
+import javax.xml.bind.JAXBElement;
+
+import org.hedgecode.xml.xspf.Attribution;
+import org.hedgecode.xml.xspf.Extension;
+import org.hedgecode.xml.xspf.Link;
+import org.hedgecode.xml.xspf.Meta;
+import org.hedgecode.xml.xspf.Track;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface PlaylistBinder extends Binder {
+
+    String getPackageName();
+
+    Class getPlaylistClass();
+
+    Object getPlaylist();
+
+    void setPlaylist(Object playlist);
+
+    public JAXBElement wrapPlaylist();
+
+    String getLocation();
+
+    void setLocation(String location);
+
+    String getIdentifier();
+
+    void setIdentifier(String identifier);
+
+    Date getDate();
+
+    void setDate(Date date);
+
+    String getLicense();
+
+    void setLicense(String license);
+
+    Attribution getAttribution();
+
+    void setAttribution(Attribution attribution);
+
+    List<Track> getTracks();
+
+    void addTrack(Track track);
+
+    String getVersion();
+
+    void setVersion(String version);
+
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/bind/RNGPlaylistBinder.java b/src/main/java/org/hedgecode/xml/xspf/bind/RNGPlaylistBinder.java
new file mode 100644 (file)
index 0000000..338d0eb
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf.bind;
+
+import java.util.ArrayList;
+import java.util.GregorianCalendar;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.bind.JAXBElement;
+import javax.xml.datatype.DatatypeConfigurationException;
+import javax.xml.datatype.DatatypeFactory;
+import javax.xml.datatype.XMLGregorianCalendar;
+import javax.xml.namespace.QName;
+
+import org.hedgecode.xml.xspf.PlaylistFactory;
+import org.hedgecode.xml.xspf.XSPFConstants;
+import org.hedgecode.xml.xspf.rng.Attribution;
+import org.hedgecode.xml.xspf.rng.Date;
+import org.hedgecode.xml.xspf.rng.Identifier;
+import org.hedgecode.xml.xspf.rng.License;
+import org.hedgecode.xml.xspf.rng.Location;
+import org.hedgecode.xml.xspf.rng.Playlist;
+import org.hedgecode.xml.xspf.rng.Track;
+import org.hedgecode.xml.xspf.rng.TrackList;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class RNGPlaylistBinder extends AbstractRNGBinder implements PlaylistBinder {
+
+    private Playlist rngPlaylist;
+
+    public RNGPlaylistBinder() {
+        this.rngPlaylist = RNG_FACTORY.createPlaylist();
+    }
+
+    public RNGPlaylistBinder(Playlist rngPlaylist) {
+        this.rngPlaylist = rngPlaylist;
+    }
+
+    @Override
+    protected Object getElement(Class elementClass) {
+        for (Object element : rngPlaylist.getTitleOrCreatorOrAnnotation()) {
+            if (element.getClass().equals(elementClass))
+                return element;
+        }
+        return null;
+    }
+
+    @Override
+    protected List<Object> getElements(Class elementClass) {
+        List<Object> elements = new ArrayList<>();
+        for (Object element : rngPlaylist.getTitleOrCreatorOrAnnotation()) {
+            if (element.getClass().equals(elementClass))
+                elements.add(element);
+        }
+        return elements;
+    }
+
+    @Override
+    protected List<Object> getAllElements() {
+        return rngPlaylist.getTitleOrCreatorOrAnnotation();
+    }
+
+    @Override
+    public String getPackageName() {
+        return rngPlaylist.getClass().getPackage().getName();
+    }
+
+    @Override
+    public Class getPlaylistClass() {
+        return rngPlaylist.getClass();
+    }
+
+    @Override
+    public Object getPlaylist() {
+        return rngPlaylist;
+    }
+
+    @Override
+    public void setPlaylist(Object playlist) {
+        if (playlist instanceof Playlist)
+            this.rngPlaylist = (Playlist) playlist;
+    }
+
+    @Override
+    public JAXBElement wrapPlaylist() {
+        return new JAXBElement<>(
+                XSPFConstants.PLAYLIST_QNAME,
+                Playlist.class,
+                rngPlaylist
+        );
+    }
+
+    @Override
+    public String getLocation() {
+        Location locationElement = (Location) getElement(Location.class);
+        return locationElement != null ? locationElement.getValue() : null;
+    }
+
+    @Override
+    public void setLocation(String location) {
+        Location locationElement = (Location) getElement(Location.class);
+        if (locationElement != null)
+            getAllElements().remove(locationElement);
+        if (location != null) {
+            locationElement = RNG_FACTORY.createLocation();
+            locationElement.setValue(location);
+            getAllElements().add(locationElement);
+        }
+    }
+
+    @Override
+    public String getIdentifier() {
+        Identifier identifierElement = (Identifier) getElement(Identifier.class);
+        return identifierElement != null ? identifierElement.getValue() : null;
+    }
+
+    @Override
+    public void setIdentifier(String identifier) {
+        Identifier identifierElement = (Identifier) getElement(Identifier.class);
+        if (identifierElement != null)
+            getAllElements().remove(identifierElement);
+        if (identifier != null) {
+            identifierElement = RNG_FACTORY.createIdentifier();
+            identifierElement.setValue(identifier);
+            getAllElements().add(identifierElement);
+        }
+    }
+
+    @Override
+    public java.util.Date getDate() {
+        Date dateElement = (Date) getElement(Date.class);
+        return dateElement != null && dateElement.getValue() != null
+                ? dateElement.getValue().toGregorianCalendar().getTime()
+                : null;
+    }
+
+    @Override
+    public void setDate(java.util.Date date) {
+        Date dateElement = (Date) getElement(Date.class);
+        if (dateElement != null)
+            getAllElements().remove(dateElement);
+        if (date != null) {
+            dateElement = RNG_FACTORY.createDate();
+            GregorianCalendar gCalendar = new GregorianCalendar();
+            gCalendar.setTime(date);
+            XMLGregorianCalendar xmlCalendar = null;
+            try {
+                xmlCalendar = DatatypeFactory.newInstance().newXMLGregorianCalendar(gCalendar);
+            } catch (DatatypeConfigurationException ignored) {}
+            dateElement.setValue(xmlCalendar);
+            getAllElements().add(dateElement);
+        }
+    }
+
+    @Override
+    public String getLicense() {
+        License licenseElement = (License) getElement(License.class);
+        return licenseElement != null ? licenseElement.getValue() : null;
+    }
+
+    @Override
+    public void setLicense(String license) {
+        License licenseElement = (License) getElement(License.class);
+        if (licenseElement != null)
+            getAllElements().remove(licenseElement);
+        if (license != null) {
+            licenseElement = RNG_FACTORY.createLicense();
+            licenseElement.setValue(license);
+            getAllElements().add(licenseElement);
+        }
+    }
+
+    @Override
+    public org.hedgecode.xml.xspf.Attribution getAttribution() {
+        Attribution attributionElement = (Attribution) getElement(Attribution.class);
+        if (attributionElement != null) {
+            org.hedgecode.xml.xspf.Attribution attribution = PlaylistFactory.createAttribution();
+            String identifier = null, location = null;
+            for (Object element : attributionElement.getIdentifierOrLocation()) {
+
+                if (element.getClass().equals(Identifier.class))
+                    identifier = ((Identifier) element).getValue();
+                else if (element.getClass().equals(Location.class))
+                    location = ((Location) element).getValue();
+
+                if (identifier != null && location != null) {
+                    attribution.add(identifier, location);
+                    identifier = null;
+                    location = null;
+                }
+            }
+            return attribution;
+        }
+        return null;
+    }
+
+    @Override
+    public void setAttribution(org.hedgecode.xml.xspf.Attribution attribution) {
+        Attribution attributionElement = (Attribution) getElement(Attribution.class);
+        if (attributionElement != null)
+            getAllElements().remove(attributionElement);
+        if (attribution != null) {
+            attributionElement = RNG_FACTORY.createAttribution();
+            for (Map.Entry<String, String> element : attribution.get().entrySet()) {
+                Location location = RNG_FACTORY.createLocation();
+                location.setValue(element.getKey());
+                Identifier identifier = RNG_FACTORY.createIdentifier();
+                identifier.setValue(element.getValue());
+                attributionElement.getIdentifierOrLocation().add(location);
+                attributionElement.getIdentifierOrLocation().add(identifier);
+            }
+            getAllElements().add(attributionElement);
+        }
+    }
+
+    @Override
+    public List<org.hedgecode.xml.xspf.Track> getTracks() {
+        List<org.hedgecode.xml.xspf.Track> tracks = new ArrayList<>();
+        TrackList traclListElement = (TrackList) getElement(TrackList.class);
+        if (traclListElement != null) {
+            for (Track track : traclListElement.getTrack()) {
+                tracks.add(
+                        PlaylistFactory.createTrack(
+                                BinderFactory.createTrackBinder(track)
+                        )
+                );
+            }
+        }
+        return tracks;
+    }
+
+    @Override
+    public void addTrack(org.hedgecode.xml.xspf.Track track) {
+        if (track != null) {
+            TrackList trackListElement = (TrackList) getElement(TrackList.class);
+            if (trackListElement == null) {
+                trackListElement = RNG_FACTORY.createTrackList();
+                getAllElements().add(trackListElement);
+            }
+
+            Track trackElement = RNG_FACTORY.createTrack();
+            PlaylistFactory.bindTrack(
+                    track,
+                    BinderFactory.createTrackBinder(trackElement)
+            );
+
+            trackListElement.getTrack().add(
+                    trackElement
+            );
+        }
+    }
+
+    @Override
+    public String getVersion() {
+        return rngPlaylist.getVersion();
+    }
+
+    @Override
+    public void setVersion(String version) {
+        rngPlaylist.setVersion(version);
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/bind/RNGTrackBinder.java b/src/main/java/org/hedgecode/xml/xspf/bind/RNGTrackBinder.java
new file mode 100644 (file)
index 0000000..d2dc95a
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf.bind;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.hedgecode.xml.xspf.rng.Album;
+import org.hedgecode.xml.xspf.rng.Duration;
+import org.hedgecode.xml.xspf.rng.Identifier;
+import org.hedgecode.xml.xspf.rng.Location;
+import org.hedgecode.xml.xspf.rng.Track;
+import org.hedgecode.xml.xspf.rng.TrackNum;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class RNGTrackBinder extends AbstractRNGBinder implements TrackBinder {
+
+    private Track rngTrack;
+
+    public RNGTrackBinder() {
+        this.rngTrack = RNG_FACTORY.createTrack();
+    }
+
+    public RNGTrackBinder(Track rngTrack) {
+        this.rngTrack = rngTrack;
+    }
+
+    @Override
+    protected Object getElement(Class elementClass) {
+        for (Object element : rngTrack.getLocationOrIdentifierOrTitle()) {
+            if (element.getClass().equals(elementClass))
+                return element;
+        }
+        return null;
+    }
+
+    @Override
+    protected List<Object> getElements(Class elementClass) {
+        List<Object> elements = new ArrayList<>();
+        for (Object element : rngTrack.getLocationOrIdentifierOrTitle()) {
+            if (element.getClass().equals(elementClass))
+                elements.add(element);
+        }
+        return elements;
+    }
+
+    @Override
+    protected List<Object> getAllElements() {
+        return rngTrack.getLocationOrIdentifierOrTitle();
+    }
+
+    @Override
+    public List<String> getLocations() {
+        List<String> locations = new ArrayList<>();
+        for (Object element : getElements(Location.class)) {
+            Location locationElement = (Location) element;
+            locations.add(
+                    locationElement.getValue()
+            );
+        }
+        return locations;
+    }
+
+    @Override
+    public void addLocation(String location) {
+        if (location != null) {
+            Location locationElement = RNG_FACTORY.createLocation();
+            locationElement.setValue(location);
+            getAllElements().add(locationElement);
+        }
+    }
+
+    @Override
+    public List<String> getIdentifiers() {
+        List<String> identifiers = new ArrayList<>();
+        for (Object element : getElements(Identifier.class)) {
+            Identifier identifierElement = (Identifier) element;
+            identifiers.add(
+                    identifierElement.getValue()
+            );
+        }
+        return identifiers;
+    }
+
+    @Override
+    public void addIdentifier(String identifier) {
+        if (identifier != null) {
+            Identifier identifierElement = RNG_FACTORY.createIdentifier();
+            identifierElement.setValue(identifier);
+            getAllElements().add(identifierElement);
+        }
+    }
+
+    @Override
+    public String getAlbum() {
+        Album albumElement = (Album) getElement(Album.class);
+        return albumElement != null ? albumElement.getContent() : null;
+    }
+
+    @Override
+    public void setAlbum(String album) {
+        Album albumElement = (Album) getElement(Album.class);
+        if (albumElement != null)
+            getAllElements().remove(albumElement);
+        if (album != null) {
+            albumElement = RNG_FACTORY.createAlbum();
+            albumElement.setContent(album);
+            getAllElements().add(albumElement);
+        }
+    }
+
+    @Override
+    public Integer getTrackNum() {
+        TrackNum trackNumElement = (TrackNum) getElement(TrackNum.class);
+        return trackNumElement != null && trackNumElement.getValue() != null
+                ? trackNumElement.getValue().intValue()
+                : null;
+    }
+
+    @Override
+    public void setTrackNum(Integer trackNum) {
+        TrackNum trackNumElement = (TrackNum) getElement(TrackNum.class);
+        if (trackNumElement != null)
+            getAllElements().remove(trackNumElement);
+        if (trackNum != null) {
+            trackNumElement = RNG_FACTORY.createTrackNum();
+            trackNumElement.setValue(
+                    BigInteger.valueOf(trackNum)
+            );
+            getAllElements().add(trackNumElement);
+        }
+    }
+
+    @Override
+    public Integer getDuration() {
+        Duration durationElement = (Duration) getElement(Duration.class);
+        return durationElement != null && durationElement.getValue() != null
+                ? durationElement.getValue().intValue()
+                : null;
+    }
+
+    @Override
+    public void setDuration(Integer duration) {
+        Duration durationElement = (Duration) getElement(Duration.class);
+        if (durationElement != null)
+            getAllElements().remove(durationElement);
+        if (duration != null) {
+            durationElement = RNG_FACTORY.createDuration();
+            durationElement.setValue(
+                    BigInteger.valueOf(duration)
+            );
+            getAllElements().add(durationElement);
+        }
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/bind/TrackBinder.java b/src/main/java/org/hedgecode/xml/xspf/bind/TrackBinder.java
new file mode 100644 (file)
index 0000000..55505c0
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf.bind;
+
+import java.util.List;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface TrackBinder extends Binder {
+
+    List<String> getLocations();
+
+    void addLocation(String location);
+
+    List<String> getIdentifiers();
+
+    void addIdentifier(String identifier);
+
+    String getAlbum();
+
+    void setAlbum(String album);
+
+    Integer getTrackNum();
+
+    void setTrackNum(Integer trackNum);
+
+    Integer getDuration();
+
+    void setDuration(Integer duration);
+
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/bind/XSDPlaylistBinder.java b/src/main/java/org/hedgecode/xml/xspf/bind/XSDPlaylistBinder.java
new file mode 100644 (file)
index 0000000..0a7a346
--- /dev/null
@@ -0,0 +1,350 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf.bind;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.bind.JAXBElement;
+import javax.xml.datatype.DatatypeConfigurationException;
+import javax.xml.datatype.DatatypeFactory;
+import javax.xml.datatype.XMLGregorianCalendar;
+import javax.xml.namespace.QName;
+
+import org.hedgecode.xml.xspf.Attribution;
+import org.hedgecode.xml.xspf.Extension;
+import org.hedgecode.xml.xspf.Link;
+import org.hedgecode.xml.xspf.Meta;
+import org.hedgecode.xml.xspf.PlaylistFactory;
+import org.hedgecode.xml.xspf.Track;
+import org.hedgecode.xml.xspf.xsd.ExtensionType;
+import org.hedgecode.xml.xspf.xsd.LinkType;
+import org.hedgecode.xml.xspf.xsd.MetaType;
+import org.hedgecode.xml.xspf.xsd.PlaylistType;
+import org.hedgecode.xml.xspf.xsd.TrackType;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class XSDPlaylistBinder extends AbstractXSDBinder implements PlaylistBinder {
+
+    private static final String REF_LOCATION_NAME = "location";
+    private static final String REF_IDENTIFIER_NAME = "identifier";
+
+    private PlaylistType xsdPlaylist;
+
+    public XSDPlaylistBinder() {
+        this.xsdPlaylist = XSD_FACTORY.createPlaylistType();
+    }
+
+    public XSDPlaylistBinder(PlaylistType xsdPlaylist) {
+        this.xsdPlaylist = xsdPlaylist;
+    }
+
+    @Override
+    public String getPackageName() {
+        return xsdPlaylist.getClass().getPackage().getName();
+    }
+
+    @Override
+    public Class getPlaylistClass() {
+        return xsdPlaylist.getClass();
+    }
+
+    @Override
+    public Object getPlaylist() {
+        return xsdPlaylist;
+    }
+
+    @Override
+    public void setPlaylist(Object playlist) {
+        if (playlist instanceof PlaylistType)
+            this.xsdPlaylist = (PlaylistType) playlist;
+    }
+
+    @Override
+    public JAXBElement wrapPlaylist() {
+        return XSD_FACTORY.createPlaylist(xsdPlaylist);
+    }
+
+    @Override
+    public String getTitle() {
+        return xsdPlaylist.getTitle();
+    }
+
+    @Override
+    public void setTitle(String title) {
+        xsdPlaylist.setTitle(title);
+    }
+
+    @Override
+    public String getCreator() {
+        return xsdPlaylist.getCreator();
+    }
+
+    @Override
+    public void setCreator(String creator) {
+        xsdPlaylist.setCreator(creator);
+    }
+
+    @Override
+    public String getAnnotation() {
+        return xsdPlaylist.getAnnotation();
+    }
+
+    @Override
+    public void setAnnotation(String annotation) {
+        xsdPlaylist.setAnnotation(annotation);
+    }
+
+    @Override
+    public String getInfo() {
+        return xsdPlaylist.getInfo();
+    }
+
+    @Override
+    public void setInfo(String info) {
+        xsdPlaylist.setInfo(info);
+    }
+
+    @Override
+    public String getLocation() {
+        return xsdPlaylist.getLocation();
+    }
+
+    @Override
+    public void setLocation(String location) {
+        xsdPlaylist.setLocation(location);
+    }
+
+    @Override
+    public String getIdentifier() {
+        return xsdPlaylist.getIdentifier();
+    }
+
+    @Override
+    public void setIdentifier(String identifier) {
+        xsdPlaylist.setIdentifier(identifier);
+    }
+
+    @Override
+    public String getImage() {
+        return xsdPlaylist.getImage();
+    }
+
+    @Override
+    public void setImage(String image) {
+        xsdPlaylist.setImage(image);
+    }
+
+    @Override
+    public Date getDate() {
+        XMLGregorianCalendar calendar = xsdPlaylist.getDate();
+        if (calendar != null)
+            return calendar.toGregorianCalendar().getTime();
+        return null;
+    }
+
+    @Override
+    public void setDate(Date date) {
+        XMLGregorianCalendar xmlCalendar = null;
+        if (date != null) {
+            GregorianCalendar gCalendar = new GregorianCalendar();
+            gCalendar.setTime(date);
+            try {
+                xmlCalendar = DatatypeFactory.newInstance().newXMLGregorianCalendar(gCalendar);
+            } catch (DatatypeConfigurationException ignored) {}
+        }
+        xsdPlaylist.setDate(xmlCalendar);
+    }
+
+    @Override
+    public String getLicense() {
+        return xsdPlaylist.getLicense();
+    }
+
+    @Override
+    public void setLicense(String license) {
+        xsdPlaylist.setLicense(license);
+    }
+
+    @Override
+    public Attribution getAttribution() {
+        if (xsdPlaylist.getAttribution() != null) {
+            List<JAXBElement<String>> elements = xsdPlaylist.getAttribution().getIdentifierOrLocation();
+            Attribution attribution = PlaylistFactory.createAttribution();
+            String identifier = null, location = null;
+            for (JAXBElement<String> element : elements) {
+
+                if (REF_IDENTIFIER_NAME.equals(element.getName().getLocalPart()))
+                    identifier = element.getValue();
+                else if (REF_LOCATION_NAME.equals(element.getName().getLocalPart()))
+                    location = element.getValue();
+
+                if (identifier != null && location != null) {
+                    attribution.add(identifier, location);
+                    identifier = null;
+                    location = null;
+                }
+            }
+            return attribution;
+        }
+        return null;
+    }
+
+    @Override
+    public void setAttribution(Attribution attribution) {
+        if (attribution != null) {
+            if (xsdPlaylist.getAttribution() == null) {
+                xsdPlaylist.setAttribution(
+                        XSD_FACTORY.createAttributionType()
+                );
+            }
+            for (Map.Entry<String, String> element : attribution.get().entrySet()) {
+                xsdPlaylist.getAttribution().getIdentifierOrLocation().add(
+                        XSD_FACTORY.createAttributionTypeIdentifier(element.getKey())
+                );
+                xsdPlaylist.getAttribution().getIdentifierOrLocation().add(
+                        XSD_FACTORY.createAttributionTypeLocation(element.getValue())
+                );
+            }
+        }
+    }
+
+    @Override
+    public List<Link> getLinks() {
+        List<Link> links = new ArrayList<>();
+        for (LinkType link : xsdPlaylist.getLink()) {
+            links.add(
+                    PlaylistFactory.createLink(link.getRel(), link.getValue())
+            );
+        }
+        return links;
+    }
+
+    @Override
+    public void addLink(Link link) {
+        if (link != null) {
+            LinkType linkType = XSD_FACTORY.createLinkType();
+            linkType.setRel(link.getRel());
+            linkType.setValue(link.getContent());
+            xsdPlaylist.getLink().add(
+                    linkType
+            );
+        }
+    }
+
+    @Override
+    public List<Meta> getMetas() {
+        List<Meta> metas = new ArrayList<>();
+        for (MetaType meta : xsdPlaylist.getMeta()) {
+            metas.add(
+                    PlaylistFactory.createMeta(meta.getRel(), meta.getValue())
+            );
+        }
+        return metas;
+    }
+
+    @Override
+    public void addMeta(Meta meta) {
+        if (meta != null) {
+            MetaType metaType = XSD_FACTORY.createMetaType();
+            metaType.setRel(meta.getRel());
+            metaType.setValue(meta.getContent());
+            xsdPlaylist.getMeta().add(
+                    metaType
+            );
+        }
+    }
+
+    @Override
+    public List<Extension> getExtensions() {
+        List<Extension> extensions = new ArrayList<>();
+        for (ExtensionType extension : xsdPlaylist.getExtension()) {
+            extensions.add(
+                    PlaylistFactory.createExtension(
+                            extension.getApplication(), extension.getContent()
+                    )
+            );
+        }
+        return extensions;
+    }
+
+    @Override
+    public void addExtension(Extension extension) {
+        if (extension != null) {
+            ExtensionType extensionType = XSD_FACTORY.createExtensionType();
+            extensionType.setApplication(extension.getApplication());
+            extensionType.getContent().addAll(extension.getContent());
+            xsdPlaylist.getExtension().add(
+                    extensionType
+            );
+        }
+    }
+
+    @Override
+    public List<Track> getTracks() {
+        List<Track> tracks = new ArrayList<>();
+        if (xsdPlaylist.getTrackList() != null) {
+            for (TrackType track : xsdPlaylist.getTrackList().getTrack()) {
+                tracks.add(
+                        PlaylistFactory.createTrack(
+                                BinderFactory.createTrackBinder(track)
+                        )
+                );
+            }
+        }
+        return tracks;
+    }
+
+    @Override
+    public void addTrack(Track track) {
+        if (track != null) {
+            if (xsdPlaylist.getTrackList() == null) {
+                xsdPlaylist.setTrackList(
+                        XSD_FACTORY.createTrackListType()
+                );
+            }
+            TrackType trackType = XSD_FACTORY.createTrackType();
+
+            PlaylistFactory.bindTrack(
+                    track,
+                    BinderFactory.createTrackBinder(trackType)
+            );
+
+            xsdPlaylist.getTrackList().getTrack().add(
+                    trackType
+            );
+        }
+    }
+
+    @Override
+    public String getVersion() {
+        return xsdPlaylist.getVersion();
+    }
+
+    @Override
+    public void setVersion(String version) {
+        xsdPlaylist.setVersion(version);
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/bind/XSDTrackBinder.java b/src/main/java/org/hedgecode/xml/xspf/bind/XSDTrackBinder.java
new file mode 100644 (file)
index 0000000..d32008c
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf.bind;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.hedgecode.xml.xspf.Extension;
+import org.hedgecode.xml.xspf.Link;
+import org.hedgecode.xml.xspf.Meta;
+import org.hedgecode.xml.xspf.PlaylistFactory;
+import org.hedgecode.xml.xspf.xsd.ExtensionType;
+import org.hedgecode.xml.xspf.xsd.LinkType;
+import org.hedgecode.xml.xspf.xsd.MetaType;
+import org.hedgecode.xml.xspf.xsd.TrackType;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class XSDTrackBinder extends AbstractXSDBinder implements TrackBinder {
+
+    private TrackType xsdTrack;
+
+    public XSDTrackBinder() {
+        this.xsdTrack = XSD_FACTORY.createTrackType();
+    }
+
+    public XSDTrackBinder(TrackType xsdTrack) {
+        this.xsdTrack = xsdTrack;
+    }
+
+    public Object getTrack() {
+        return xsdTrack;
+    }
+
+    public void setTrack(Object track) {
+        if (track != null && track instanceof TrackType)
+            this.xsdTrack = (TrackType) track;
+    }
+
+    @Override
+    public List<String> getLocations() {
+        return xsdTrack.getLocation();
+    }
+
+    @Override
+    public void addLocation(String location) {
+        if (location != null) {
+            xsdTrack.getLocation().add(
+                    location
+            );
+        }
+    }
+
+    @Override
+    public List<String> getIdentifiers() {
+        return xsdTrack.getIdentifier();
+    }
+
+    @Override
+    public void addIdentifier(String identifier) {
+        if (identifier != null) {
+            xsdTrack.getIdentifier().add(
+                    identifier
+            );
+        }
+    }
+
+    @Override
+    public String getTitle() {
+        return xsdTrack.getTitle();
+    }
+
+    @Override
+    public void setTitle(String title) {
+        xsdTrack.setTitle(title);
+    }
+
+    @Override
+    public String getCreator() {
+        return xsdTrack.getCreator();
+    }
+
+    @Override
+    public void setCreator(String creator) {
+        xsdTrack.setCreator(creator);
+    }
+
+    @Override
+    public String getAnnotation() {
+        return xsdTrack.getAnnotation();
+    }
+
+    @Override
+    public void setAnnotation(String annotation) {
+        xsdTrack.setAnnotation(annotation);
+    }
+
+    @Override
+    public String getInfo() {
+        return xsdTrack.getInfo();
+    }
+
+    @Override
+    public void setInfo(String info) {
+        xsdTrack.setInfo(info);
+    }
+
+    @Override
+    public String getImage() {
+        return xsdTrack.getImage();
+    }
+
+    @Override
+    public void setImage(String image) {
+        xsdTrack.setImage(image);
+    }
+
+    @Override
+    public String getAlbum() {
+        return xsdTrack.getAlbum();
+    }
+
+    @Override
+    public void setAlbum(String album) {
+        xsdTrack.setAlbum(album);
+    }
+
+    @Override
+    public Integer getTrackNum() {
+        BigInteger trackNum = xsdTrack.getTrackNum();
+        return trackNum != null ? trackNum.intValue() : null;
+    }
+
+    @Override
+    public void setTrackNum(Integer trackNum) {
+        xsdTrack.setTrackNum(
+                trackNum != null ? BigInteger.valueOf(trackNum) : null
+        );
+    }
+
+    @Override
+    public Integer getDuration() {
+        BigInteger duration = xsdTrack.getDuration();
+        return duration != null ? duration.intValue() : null;
+    }
+
+    @Override
+    public void setDuration(Integer duration) {
+        xsdTrack.setDuration(
+                duration != null ? BigInteger.valueOf(duration) : null
+        );
+    }
+
+    @Override
+    public List<Link> getLinks() {
+        List<Link> links = new ArrayList<>();
+        for (LinkType link : xsdTrack.getLink()) {
+            links.add(
+                    PlaylistFactory.createLink(link.getRel(), link.getValue())
+            );
+        }
+        return links;
+    }
+
+    @Override
+    public void addLink(Link link) {
+        if (link != null) {
+            LinkType linkType = XSD_FACTORY.createLinkType();
+            linkType.setRel(link.getRel());
+            linkType.setValue(link.getContent());
+            xsdTrack.getLink().add(
+                    linkType
+            );
+        }
+    }
+
+    @Override
+    public List<Meta> getMetas() {
+        List<Meta> metas = new ArrayList<>();
+        for (MetaType meta : xsdTrack.getMeta()) {
+            metas.add(
+                    PlaylistFactory.createMeta(meta.getRel(), meta.getValue())
+            );
+        }
+        return metas;
+    }
+
+    @Override
+    public void addMeta(Meta meta) {
+        if (meta != null) {
+            MetaType metaType = XSD_FACTORY.createMetaType();
+            metaType.setRel(meta.getRel());
+            metaType.setValue(meta.getContent());
+            xsdTrack.getMeta().add(
+                    metaType
+            );
+        }
+    }
+
+    @Override
+    public List<Extension> getExtensions() {
+        List<Extension> extensions = new ArrayList<>();
+        for (ExtensionType extension : xsdTrack.getExtension()) {
+            extensions.add(
+                    PlaylistFactory.createExtension(
+                            extension.getApplication(), extension.getContent()
+                    )
+            );
+        }
+        return extensions;
+    }
+
+    @Override
+    public void addExtension(Extension extension) {
+        if (extension != null) {
+            ExtensionType extensionType = XSD_FACTORY.createExtensionType();
+            extensionType.setApplication(extension.getApplication());
+            extensionType.getContent().addAll(extension.getContent());
+            xsdTrack.getExtension().add(
+                    extensionType
+            );
+        }
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/validate/EmptyValidator.java b/src/main/java/org/hedgecode/xml/xspf/validate/EmptyValidator.java
new file mode 100644 (file)
index 0000000..ee95d1f
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf.validate;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class EmptyValidator implements Validator {
+
+    private EmptyValidator() {
+    }
+
+    public static Validator getInstance() {
+        return new EmptyValidator();
+    }
+
+    @Override
+    public void validate(Object validateObj) {
+
+    }
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/validate/URIValidator.java b/src/main/java/org/hedgecode/xml/xspf/validate/URIValidator.java
new file mode 100644 (file)
index 0000000..4c78479
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf.validate;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class URIValidator implements Validator {
+
+    private URIValidator() {
+    }
+
+    public static Validator getInstance() {
+        return new URIValidator();
+    }
+
+    @Override
+    public void validate(Object validateObj) {
+
+    }
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/validate/Validate.java b/src/main/java/org/hedgecode/xml/xspf/validate/Validate.java
new file mode 100644 (file)
index 0000000..4303fa2
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf.validate;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@Target({TYPE, FIELD, METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Validate {
+
+    ValidatorType validatorType();
+
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/validate/ValidateException.java b/src/main/java/org/hedgecode/xml/xspf/validate/ValidateException.java
new file mode 100644 (file)
index 0000000..e02f358
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf.validate;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class ValidateException extends Exception {
+
+    private int code;
+
+    public ValidateException(int code, String message) {
+        super(message);
+        this.code = code;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/validate/Validator.java b/src/main/java/org/hedgecode/xml/xspf/validate/Validator.java
new file mode 100644 (file)
index 0000000..496de85
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf.validate;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public interface Validator {
+
+    void validate(Object validateObj);
+
+}
diff --git a/src/main/java/org/hedgecode/xml/xspf/validate/ValidatorType.java b/src/main/java/org/hedgecode/xml/xspf/validate/ValidatorType.java
new file mode 100644 (file)
index 0000000..40d62b0
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf.validate;
+
+/**
+ *
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public enum ValidatorType {
+
+    EMPTY ( EmptyValidator.getInstance() ),
+    URI   ( URIValidator.getInstance() );
+
+    private Validator validator;
+
+    private ValidatorType(Validator validator) {
+        this.validator = validator;
+    }
+
+    public Validator getValidator() {
+        return validator;
+    }
+
+}
diff --git a/src/main/rng/xspf-1_0.7.rng b/src/main/rng/xspf-1_0.7.rng
new file mode 100644 (file)
index 0000000..0e693d6
--- /dev/null
@@ -0,0 +1,333 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- ==================================================================
+     Relax NG Schema for XSPF Version 1
+
+     $Id$
+
+     This schema was written as a service for developers who want
+     to validate the playlists generated by their applications.
+     Nobody claims it is perfect, so if you find bugs please report
+     them. In any case, the spec overrides this schema.
+
+     Further information about XSPF is available at http://www.xml.org
+
+
+     Copyright (c) 2005 Matthias Friedrich <matt@mafr.de>
+     Copyright (c) 2007-2008 Sebastian Pipping <webmaster@hartwork.org>
+
+     The schema is released under the Creative Commons 
+     Attribution-ShareAlike 2.5 license.
+
+     http://creativecommons.org/licenses/by-sa/2.5/
+
+
+     ChangeLog:
+      * 2008-09-03: Allow xml:base attribute everywhere
+                    Extract common elements
+                    Sebastian Pipping <webmaster@hartwork.org>
+
+      * 2008-07-26: Support for xml:base attribute
+                    Sebastian Pipping <webmaster@hartwork.org>
+
+      * 2007-02-16: Trang-friendly transformation of def_anything
+                    Sebastian Pipping <webmaster@hartwork.org>
+
+      * 2007-02-16: Interleave wrap for track and playlist
+                    to allow arbitrary element order
+                    Sebastian Pipping <webmaster@hartwork.org>
+
+      * 2007-01-07: The info element's content has been changed from
+                    type "text" to "anyURI", as per spec.
+                    Matthias Friedrich <matt@mafr.de>
+
+      * 2006-09-25: Fixed an error: the "attribution" element now allows
+                    "identifier" child element elements, too.
+                    Matthias Friedrich <matt@mafr.de>
+     ================================================================== -->
+
+
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+         datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"
+         ns="http://xspf.org/ns/0/">
+
+
+    <start>
+        <ref name="def_playlist-element"/>
+    </start>
+
+
+    <define name="xml_base-attribute">
+        <optional>
+            <attribute name="xml:base">
+                <data type="anyURI"/>
+            </attribute>
+        </optional>
+    </define>
+
+
+    <define name="def_title-element">
+        <element name="title">
+            <ref name="xml_base-attribute"/>
+            <text/>
+        </element>
+    </define>
+
+
+    <define name="def_creator-element">
+        <element name="creator">
+            <ref name="xml_base-attribute"/>
+            <text/>
+        </element>
+    </define>
+
+
+    <define name="def_annotation-element">
+        <element name="annotation">
+            <ref name="xml_base-attribute"/>
+            <text/>
+        </element>
+    </define>
+
+
+    <define name="def_info-element">
+        <element name="info">
+            <ref name="xml_base-attribute"/>
+            <data type="anyURI"/>
+        </element>
+    </define>
+
+
+    <define name="def_location-element">
+        <element name="location">
+            <ref name="xml_base-attribute"/>
+            <data type="anyURI"/>
+        </element>
+    </define>
+
+
+    <define name="def_identifier-element">
+        <element name="identifier">
+            <ref name="xml_base-attribute"/>
+            <data type="anyURI"/>
+        </element>
+    </define>
+
+
+    <define name="def_image-element">
+        <element name="image">
+            <ref name="xml_base-attribute"/>
+            <data type="anyURI"/>
+        </element>
+    </define>
+
+
+    <define name="def_link-element">
+        <element name="link">
+            <attribute name="rel">
+                <data type="anyURI"/>
+            </attribute>
+            <ref name="xml_base-attribute"/>
+
+            <data type="anyURI"/>
+        </element>
+    </define>
+
+
+    <define name="def_meta-element">
+        <element name="meta">
+            <attribute name="rel">
+                <data type="anyURI"/>
+            </attribute>
+            <ref name="xml_base-attribute"/>
+
+            <text />
+        </element>
+    </define>
+
+
+    <define name="def_extension-element">
+        <element name="extension">
+            <attribute name="application">
+                <data type="anyURI"/>
+            </attribute>
+            <ref name="xml_base-attribute"/>
+
+            <zeroOrMore>
+                <choice>
+                    <element>
+                        <anyName/>
+                        <ref name="def_anything"/>
+                    </element>
+                    <text/>
+                </choice>
+            </zeroOrMore>
+        </element>
+    </define>
+
+
+    <define name="def_playlist-element">
+        <element name="playlist">
+            <attribute name="version">
+                <data type="string">
+                    <param name="pattern">1</param>
+                </data>
+            </attribute>
+            <ref name="xml_base-attribute"/>
+
+            <interleave>
+                <optional>
+                    <ref name="def_title-element"/>
+                </optional>
+                <optional>
+                    <ref name="def_creator-element"/>
+                </optional>
+                <optional>
+                    <ref name="def_annotation-element"/>
+                </optional>
+                <optional>
+                    <ref name="def_info-element"/>
+                </optional>
+                <optional>
+                    <ref name="def_location-element"/>
+                </optional>
+                <optional>
+                    <ref name="def_identifier-element"/>
+                </optional>
+                <optional>
+                    <ref name="def_image-element"/>
+                </optional>
+                <optional>
+                    <element name="date">
+                        <ref name="xml_base-attribute"/>
+                        <data type="dateTime"/>
+                    </element>
+                </optional>
+                <optional>
+                    <element name="license">
+                        <ref name="xml_base-attribute"/>
+                        <data type="anyURI"/>
+                    </element>
+                </optional>
+
+                <optional>
+                    <ref name="def_attribution-element"/>
+                </optional>
+
+                <zeroOrMore>
+                    <ref name="def_link-element"/>
+                </zeroOrMore>
+                <zeroOrMore>
+                    <ref name="def_meta-element"/>
+                </zeroOrMore>
+                <zeroOrMore>
+                    <ref name="def_extension-element"/>
+                </zeroOrMore>
+
+                <element name="trackList">
+                    <ref name="xml_base-attribute"/>
+                    <zeroOrMore>
+                        <ref name="def_track-element"/>
+                    </zeroOrMore>
+                </element>
+            </interleave>
+        </element>
+    </define>
+
+
+    <define name="def_attribution-element">
+        <element name="attribution">
+            <ref name="xml_base-attribute"/>
+
+            <zeroOrMore>
+                <choice>
+                    <ref name="def_identifier-element"/>
+                    <ref name="def_location-element"/>
+                </choice>
+            </zeroOrMore>
+        </element>
+    </define>
+
+
+    <define name="def_anything">
+        <interleave>
+            <zeroOrMore>
+                <element>
+                    <anyName/>
+                    <ref name="def_anything"/>
+                </element>
+            </zeroOrMore>
+
+            <zeroOrMore>
+                <attribute>
+                    <anyName/>
+                </attribute>
+            </zeroOrMore>
+
+            <zeroOrMore>
+                <text/>
+            </zeroOrMore>
+        </interleave>
+    </define>
+
+
+    <define name="def_track-element">
+        <element name="track">
+            <ref name="xml_base-attribute"/>
+
+            <interleave>
+                <zeroOrMore>
+                    <ref name="def_location-element"/>
+                </zeroOrMore>
+                <zeroOrMore>
+                    <ref name="def_identifier-element"/>
+                </zeroOrMore>
+
+                <optional>
+                    <ref name="def_title-element"/>
+                </optional>
+                <optional>
+                    <ref name="def_creator-element"/>
+                </optional>
+                <optional>
+                    <ref name="def_annotation-element"/>
+                </optional>
+                <optional>
+                    <ref name="def_info-element"/>
+                </optional>
+                <optional>
+                    <ref name="def_image-element"/>
+                </optional>
+                <optional>
+                    <element name="album">
+                        <ref name="xml_base-attribute"/>
+                        <text/>
+                    </element>
+                </optional>
+                <optional>
+                    <element name="trackNum">
+                        <ref name="xml_base-attribute"/>
+                        <data type="nonNegativeInteger"/>
+                    </element>
+                </optional>
+                <optional>
+                    <element name="duration">
+                        <ref name="xml_base-attribute"/>
+                        <data type="nonNegativeInteger"/>
+                    </element>
+                </optional>
+
+                <zeroOrMore>
+                    <ref name="def_link-element"/>
+                </zeroOrMore>
+                <zeroOrMore>
+                    <ref name="def_meta-element"/>
+                </zeroOrMore>
+                <zeroOrMore>
+                    <ref name="def_extension-element"/>
+                </zeroOrMore>
+            </interleave>
+        </element>
+    </define>
+
+</grammar>
diff --git a/src/main/xsd/xspf-1_0.2.xsd b/src/main/xsd/xspf-1_0.2.xsd
new file mode 100644 (file)
index 0000000..84b0584
--- /dev/null
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- ==================================================================
+     XML Schema for XSPF Version 1
+
+     $Id$
+
+     This schema was written as a service for developers who want
+     to validate the playlists generated by their applications.
+     Nobody claims it is perfect, so if you find bugs please report
+     them. In any case, the spec overrides this schema.
+
+     Further information about XSPF is available at http://www.xml.org
+
+
+     Copyright (c) 2005 Matthias Friedrich <matt@mafr.de>
+
+     The schema is released under the Creative Commons
+     Attribution-ShareAlike 2.0 license.
+
+     http://creativecommons.org/licenses/by-sa/2.0/
+
+
+     ChangeLog:
+          * 2005-10-12: Fixed an error: the "extension" element now
+                       has an "application" attribute, like in the spec.
+          * 2006-09-25: Fixed an error: the "attribution" element now allows
+                       "identifier" child element elements, too.
+     ================================================================== -->
+
+<xsd:schema xmlns:xsd = "http://www.w3.org/2001/XMLSchema"
+            xmlns:xspf = "http://xspf.org/ns/0/"
+            targetNamespace="http://xspf.org/ns/0/"
+           elementFormDefault="qualified"
+           attributeFormDefault="unqualified">
+
+<xsd:element name="playlist" type="xspf:PlaylistType"/>
+
+<xsd:complexType name="PlaylistType">
+  <xsd:sequence>
+    <xsd:element name="title" type="xsd:string" minOccurs="0" maxOccurs="1"/>
+    <xsd:element name="creator" type="xsd:string" minOccurs="0" maxOccurs="1"/>
+    <xsd:element name="annotation" type="xsd:string" minOccurs="0" maxOccurs="1"/>
+    <xsd:element name="info" type="xsd:anyURI" minOccurs="0" maxOccurs="1"/>
+    <xsd:element name="location" type="xsd:anyURI" minOccurs="0" maxOccurs="1"/>
+    <xsd:element name="identifier" type="xsd:anyURI" minOccurs="0" maxOccurs="1"/>
+    <xsd:element name="image" type="xsd:anyURI" minOccurs="0" maxOccurs="1"/>
+    <xsd:element name="date" type="xsd:dateTime" minOccurs="0" maxOccurs="1"/>
+    <xsd:element name="license" type="xsd:anyURI" minOccurs="0" maxOccurs="1"/>
+    <xsd:element name="attribution" type="xspf:AttributionType" minOccurs="0" maxOccurs="1"/>
+
+    <xsd:element name="link" type="xspf:LinkType" minOccurs="0" maxOccurs="unbounded"/>
+    <xsd:element name="meta" type="xspf:MetaType" minOccurs="0" maxOccurs="unbounded"/>
+    <xsd:element name="extension" type="xspf:ExtensionType" minOccurs="0" maxOccurs="unbounded"/>
+
+    <xsd:element name="trackList" type="xspf:TrackListType" minOccurs="1" maxOccurs="1"/>
+  </xsd:sequence>
+  <xsd:attribute name="version" type="xspf:VersionType" use="required"/>
+</xsd:complexType>
+
+<xsd:simpleType name="VersionType">
+  <xsd:restriction base="xsd:string">
+    <xsd:pattern value="1"/>
+  </xsd:restriction>
+</xsd:simpleType>
+
+<xsd:complexType name="AttributionType">
+  <xsd:choice minOccurs="0" maxOccurs="unbounded">
+    <xsd:element name="identifier" type="xsd:anyURI"/>
+    <xsd:element name="location" type="xsd:anyURI"/>
+  </xsd:choice>
+</xsd:complexType>
+
+<xsd:complexType name="LinkType">
+  <xsd:simpleContent>
+    <xsd:extension base="xsd:anyURI">
+      <xsd:attribute name="rel" type="xsd:anyURI" use="required"/>
+    </xsd:extension>
+  </xsd:simpleContent>
+</xsd:complexType>
+
+<xsd:complexType name="MetaType">
+  <xsd:simpleContent>
+    <xsd:extension base="xsd:string">
+      <xsd:attribute name="rel" type="xsd:anyURI" use="required"/>
+    </xsd:extension>
+  </xsd:simpleContent>
+</xsd:complexType>
+
+<xsd:complexType name="ExtensionType">
+  <xsd:complexContent mixed="true">
+    <xsd:restriction base="xsd:anyType">
+      <xsd:sequence>
+        <xsd:any processContents="skip" minOccurs="0" maxOccurs="unbounded"/>
+      </xsd:sequence>
+      <xsd:attribute name="application" type="xsd:anyURI" use="required"/>
+    </xsd:restriction>
+  </xsd:complexContent>
+</xsd:complexType>
+
+<xsd:complexType name="TrackListType">
+  <xsd:sequence>
+    <xsd:element name="track" type="xspf:TrackType" minOccurs="0" maxOccurs="unbounded"/>
+  </xsd:sequence>
+</xsd:complexType>
+
+<xsd:complexType name="TrackType">
+  <xsd:sequence>
+    <xsd:element name="location" type="xsd:anyURI" minOccurs="0" maxOccurs="unbounded"/>
+    <xsd:element name="identifier" type="xsd:anyURI" minOccurs="0" maxOccurs="unbounded"/>
+    <xsd:element name="title" type="xsd:string" minOccurs="0" maxOccurs="1"/>
+    <xsd:element name="creator" type="xsd:string" minOccurs="0" maxOccurs="1"/>
+    <xsd:element name="annotation" type="xsd:string" minOccurs="0" maxOccurs="1"/>
+    <xsd:element name="info" type="xsd:anyURI" minOccurs="0" maxOccurs="1"/>
+    <xsd:element name="image" type="xsd:anyURI" minOccurs="0" maxOccurs="1"/>
+    <xsd:element name="album" type="xsd:string" minOccurs="0" maxOccurs="1"/>
+    <xsd:element name="trackNum" type="xsd:nonNegativeInteger" minOccurs="0" maxOccurs="1"/>
+    <xsd:element name="duration" type="xsd:nonNegativeInteger" minOccurs="0" maxOccurs="1"/>
+
+    <xsd:element name="link" type="xspf:LinkType" minOccurs="0" maxOccurs="unbounded"/>
+    <xsd:element name="meta" type="xspf:MetaType" minOccurs="0" maxOccurs="unbounded"/>
+    <xsd:element name="extension" type="xspf:ExtensionType" minOccurs="0" maxOccurs="unbounded"/>
+  </xsd:sequence>
+</xsd:complexType>
+
+</xsd:schema>
diff --git a/src/test/java/org/hedgecode/xml/xspf/XSPFTest.java b/src/test/java/org/hedgecode/xml/xspf/XSPFTest.java
new file mode 100644 (file)
index 0000000..aedc50b
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2015. Developed by Hedgecode.
+ *
+ * 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.hedgecode.xml.xspf;
+
+import java.io.File;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests for {@link XSPF}.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+@RunWith(JUnit4.class)
+public class XSPFTest extends Assert {
+
+    private static final String XSPF_FILE_DIR = "/";
+    private static final String XSPF_PLAYLIST_FILE = "playlist.xspf";
+    private static final String XSPF_TORRENT_FILE = "torrent-tv.ru.xspf";
+
+    @Test
+    public void testBind() throws Exception {
+
+        File xspfFile = new File(
+                XSPFTest.class.getResource(
+                        XSPF_FILE_DIR + XSPF_PLAYLIST_FILE
+                ).toURI()
+        );
+
+
+        Playlist playlist = XSPF.create(xspfFile);
+
+        playlist.write(
+                new File("test.xspf")
+        );
+    }
+
+    @Test
+    public void testCreate() throws Exception {
+
+        Playlist playlist = XSPF.create(XSPFConstants.Format.XSD);
+        playlist.setTitle("Title");
+        playlist.setLocation("Location");
+        XSPF.addTrack(playlist, "a.mp3", "First MP3");
+        XSPF.addTrack(playlist, "b.mp3", "Second MP3");
+        XSPF.addTrack(playlist, "c.mp3", "Third MP3");
+        playlist.write(
+                new File("test2.xspf")
+        );
+    }
+
+}
diff --git a/src/test/resources/playlist.xspf b/src/test/resources/playlist.xspf
new file mode 100644 (file)
index 0000000..ff0936e
--- /dev/null
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<playlist version="1" xmlns="http://xspf.org/ns/0/">
+  <title>My playlist</title>
+  <creator>Jane Doe</creator>
+  <annotation>My favorite songs</annotation>
+  <info>http://example.com/myplaylists</info>
+  <location>http://example.com/myplaylists/myplaylist</location>
+  <identifier>magnet:?xt=urn:sha1:YNCKHTQCWBTRNJIV4WNAE52SJUQCZO5C</identifier>
+  <image>http://example.com/img/mypicture</image>
+  <date>2005-01-08T17:10:47-05:00</date>
+  <license>http://creativecommons.org/licenses/by/1.0/</license>
+  <attribution>
+    <identifier>http://bar.com/secondderived.xspf</identifier>
+    <location>http://foo.com/original.xspf</location>
+  </attribution>
+  <link rel="http://foaf.example.org/namespace/version1">http://socialnetwork.example.org/foaf/mary.rdfs</link>
+  <meta rel="http://example.org/key">value</meta>
+  <extension application="http://example.com">
+    <clip start="25000" end="34500"/>
+  </extension>
+  <trackList>
+    <track>
+      <location>http://example.com/my.mp3</location>
+      <identifier>magnet:?xt=urn:sha1:YNCKHTQCWBTRNJIV4WNAE52SJUQCZO5C</identifier>
+      <title>My Way</title>
+      <creator>Frank Sinatra</creator>
+      <annotation>This is my theme song.</annotation>
+      <info>http://franksinatra.com/myway</info>
+      <image>http://franksinatra.com/img/myway</image>
+      <album>Frank Sinatra's Greatest Hits</album>
+      <trackNum>3</trackNum>
+      <duration>19200</duration>
+      <link rel="http://foaf.org/namespace/version1">http://socialnetwork.org/foaf/mary.rdfs</link>
+      <meta rel="http://example.org/key">value</meta>
+      <extension application="http://example.com">
+        <clip start="25000" end="34500"/>
+      </extension>
+    </track>
+  </trackList>
+</playlist>
\ No newline at end of file
diff --git a/src/test/resources/torrent-tv.ru.xspf b/src/test/resources/torrent-tv.ru.xspf
new file mode 100644 (file)
index 0000000..abcaeb0
--- /dev/null
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><playlist xmlns="http://xspf.org/ns/0/" version=""><annotation>Избранные каналы TORRENT-TV.RU</annotation><trackList xmlns="http://xspf.org/ns/0/"><track xmlns="http://xspf.org/ns/0/"><location>http://content.torrent-tv.ru/djY5Y2h1MFcrS0FWQUR0dlp1SGp2ZTJudmNlTCs1ajJLVHpDbFhIS3hLWGtId0d0ako2c0RiaFd0K1d5TkpKLzgrWFdLbzVBZVI3ZXo1aEpKNVNOUkE9PQ/cdn/5_reg.acelive</location><title>Eurosport</title></track><track xmlns="http://xspf.org/ns/0/"><location>http://content.torrent-tv.ru/ZWcrTkgwY3FXZURPVkdjZkEvU1ErVXFqeWFXRE1Ed1Rzd045TUxnR1JRTG9GbEY1dGwxa013OWRDQ1ovUnFTQlNvSGMxcWtVQlh4OWkrOVZ6QVA4MGc9PQ/cdn/6_reg.acelive</location><title>Eurospоrt 2</title></track><track xmlns="http://xspf.org/ns/0/"><location>http://content.torrent-tv.ru/Q1lWeUt0VnJXYnU1WFliNWhKd2xwWE91MUJsSGRCQVV6YzNyK3VFWTJRajhJUVVpTDY3RjR2eDNxV1ZCbUpDb0w1aUFjcXg1UC85YWt6clZhZTRKd1E9PQ/13250.acelive</location><title>Eurospоrt 2 North-East</title></track><track xmlns="http://xspf.org/ns/0/"><location>http://content.torrent-tv.ru/VVhaVmNyL05vb0pBUS9tUGFTWDh1ZE45Q25yTUpWWVFORlhTYUx2T2lPL2ozUFNISzR5MXkrVXBFZ1VJblMzckVScHpZTXYxWktudGVwSWwwYzgrZmc9PQ/cdn/38_reg.acelive</location><title>НТВ+ Спорт</title></track><track xmlns="http://xspf.org/ns/0/"><location>http://content.torrent-tv.ru/U0NYRG16NTR3a0lKWlNkOEdtT3RoT1czVVk4YjdRanpKbmtMbit0UTJuSnhhdzFnOXhqejZxK0QyYy9mSjdzVEdweEpVWnExd3ZPc3gxOVZpZmE2cXc9PQ/cdn/222_reg.acelive</location><title>НТВ+ Спорт плюс</title></track><track xmlns="http://xspf.org/ns/0/"><location>http://content.torrent-tv.ru/K2ZVWDZEeW9GR3FrZHppeCsrQTJZeU9PUGROa2dDeTRxdU5VeDRwY3JMVlFhRk0vaXAxTG50Z28yZmxLV0JHR2xrTG1XM3pjMm5INUJPQ0xEZ0tlN3c9PQ/cdn/172_reg.acelive</location><title>Eurosport HD</title></track><track xmlns="http://xspf.org/ns/0/"><location>http://content.torrent-tv.ru/eUh5WHVlbEcyenUyTVJOeGFMNWNZeGlUdHlBU1hwWTJRY2JzTEdXZU81WGR0ME96dUltQ2w5UFNzWElyRnNSZEFJU3FROHA0UDM3My96a3h0SHJhUnc9PQ/11160.acelive</location><title>Eurospоrt 2 North-East HD</title></track><track xmlns="http://xspf.org/ns/0/"><location>http://content.torrent-tv.ru/VElRQ2tIRHdyelk4RDloOTlWaXlsZDd5RTlZd1Jlck1DVkVMcVo1RmpTamFROFNQcFVZOW0yU0hDenh6T0EyUVAxdUtsUlliMlpRVmlHSVRuYUUxQ1E9PQ/cdn/625_reg.acelive</location><title>НТВ+ Футбол 1 HD</title></track><track xmlns="http://xspf.org/ns/0/"><location>http://content.torrent-tv.ru/MHdOeXdMQlNPVSt1TDIrcFlyWnFmOTJxbWJ5QlR0YmVsd2JkdlppK1dwbmVDYnE5VzlIWGlOZklHYlR3eHpYUHlmZTNwN3poY05vaTN1bWJqQnNxSFE9PQ/cdn/611_reg.acelive</location><title>НТВ+ Футбол 2 HD</title></track><track xmlns="http://xspf.org/ns/0/"><location>http://content.torrent-tv.ru/N1NVQ1dWdmlkelpqQVlyYVNCbkFoQ2dBM0FoWlpQaHRmdzRpTlRVN1lDbm5ZU2EzTEVPc0tNOU9ONWV6Q0k2TUdyd3dGaHc3OU0zWlVLdFk1RkRQUXc9PQ/cdn/646_reg.acelive</location><title>НТВ+ Футбол 3 HD</title></track><track xmlns="http://xspf.org/ns/0/"><location>http://content.torrent-tv.ru/RzNvVStPUTFNUGFyZGFtRkR5N1dLUExwdzR6NGs0Q0ZIYmNqcVdydVByTnlSTlRVSDcxWGMrTjZNNmxocUJleXozRmxlUkd2YkNqZXpLcS95b0QvdWc9PQ/cdn/473_reg.acelive</location><title>Спорт</title></track><track xmlns="http://xspf.org/ns/0/"><location>http://content.torrent-tv.ru/Rm9ZbWZGN1lFbnJtRGtnem5ScSsrQjA4M1FudTh3dlBkcmxKK2dHMXFaUVhZMHYwd29LMFA4aHhqTHBKbkJtaUxWUS9jNCtnMzR1WXhaakZ1SmdHalE9PQ/cdn/526_reg.acelive</location><title>Спорт 1 HD</title></track><track xmlns="http://xspf.org/ns/0/"><location>http://content.torrent-tv.ru/NUhOTlZXa3QxSnI1SzVUdFR2K2VQV3JGWTZVMTN0SHNsTFVKcHZERVFOU29zTDh3bWV0bHZDc2lWU0NqQURqTVpPazlzNlB0RGg0dHhjN0l0T0RXdHc9PQ/cdn/112_reg.acelive</location><title>HD Спорт</title></track><track xmlns="http://xspf.org/ns/0/"><location>http://content.torrent-tv.ru/QjVVeFlzWStIWnZrYUpVTkl1eCt1ZXVtWXJIMEhXOUxBYWc5cGYzMUFYWVlRQ3FlV2gxK0dOcmx1OHlCUmtneGQ5dlBiMHJVQ3Q1RFRsZ1AwQ2Mzc3c9PQ/cdn/538_reg.acelive</location><title>КХЛ HD</title></track><track xmlns="http://xspf.org/ns/0/"><location>http://content.torrent-tv.ru/dUlQSjB4L0NZMTN1YjlUcHJQS0VpZ0YyMmtodUI3bVZGTWJiR2xuS3RPZDhyWGF4M2tzbGhvaFhHbmQzWU1Fb1VOeENmbThRUGVDMDNBMVVTU2Izcmc9PQ/cdn/487_all.acelive</location><title>Paramount Comedy</title></track><track xmlns="http://xspf.org/ns/0/"><location>http://content.torrent-tv.ru/eDEwazVwZWFXS0tPZXNPeVc0THljdFM5aTZNTlRRSmYwQ3pPcW1iWkY2djZsb2g1RE9PT0YrNzNLNXFVd0RnUitkWnNHcGIvTUMrbWhIUGY1NFVGSlE9PQ/11181.acelive</location><title>Paramount Comedy HD</title></track><track xmlns="http://xspf.org/ns/0/"><location>http://content.torrent-tv.ru/U0NrTzNaMFZ1U1F6ODMwbVppQXFEdXNzUkVNM1UrV24wR3FVMmdISzRoM3NLNHdoMFhSajh5aEFVQkNtYzgvSkZHdFhlZ0NtOXBFUG91UG9lVmxjZWc9PQ/cdn/28_reg.acelive</location><title>Перец</title></track><track xmlns="http://xspf.org/ns/0/"><location>http://content.torrent-tv.ru/cWpKZHZjbHRiZnRleGx4YzhudjI2N3lNSVMwREVCS2VralBHbk9mMTZZK3JHM05HU3gzOGtJSzlZMUJ3dy84Yng0ZEI4M2Z1MGRjRHo1YlpJTXdrdEE9PQ/cdn/731_reg.acelive</location><title>Мульт</title></track><track xmlns="http://xspf.org/ns/0/"><location>http://content.torrent-tv.ru/ZlJ5ZXBkOEd0SG56R1JFdzdyWC8zT2RSNlV0aEN1M1FUTHB4RzVyekJQajMvNXZ3R3Bsc3BkbGQwQytHb3pJdzZhR3FoeEFyVW81OG1kS1kzQXdzN0E9PQ/cdn/69_reg.acelive</location><title>TV 1000</title></track><track xmlns="http://xspf.org/ns/0/"><location>http://content.torrent-tv.ru/V3ppeE12NkFBbE5rNkk0blM3V2ZrMjc3b09jVWFoMjYzb3BLSTJQS3ZwN2c4M2Vxc0drVTlRQjdrRXlCaTAvdzNBKzNHbHUxOGJydTFLOWRXSFkzTUE9PQ/cdn/118_reg.acelive</location><title>TV 1000 Русское кино</title></track><track xmlns="http://xspf.org/ns/0/"><location>http://content.torrent-tv.ru/dGJobWkvQXpseTk2ZFUwSXFXWkJIT2c4dDlpSGpSZUdhcHFiWTk2Qm0zRVZJbG9jVmdzamdCTjVWVm9wS0dIbFVvNlhzblJ1NXA5djROditncVUrdFE9PQ/cdn/557_reg.acelive</location><title>TV1000 Comedy HD</title></track><track xmlns="http://xspf.org/ns/0/"><location>http://content.torrent-tv.ru/Zkx6VUJPSE04OVJyNEhqOU9pVGx4ZmRFMHRhZWtud1c2S2psRmt4VlJvZlBjczRtU0J2N0RxU0ZCRlFmcWNtVVpQeUpQQzAwT3hLdTFuS0lJSGU3cWc9PQ/cdn/36_reg.acelive</location><title>НТВ+ Наше кино</title></track><track xmlns="http://xspf.org/ns/0/"><location>http://content.torrent-tv.ru/aTBnUEUzbXlNa1BpK0xwSDFPcnNsYmhlbDV0aW9INGpDTG9nZ0Z4ckFXcko1VEJwbVZhVitVQjRBcjVGZlE3UGw1eGtpMUJ1TVZ3SjNXV2t2NlV3ZWc9PQ/cdn/228_reg.acelive</location><title>НТВ+ Кино плюс</title></track><track xmlns="http://xspf.org/ns/0/"><location>http://content.torrent-tv.ru/TzJrNXFUdC9DRUR4bDRaSjVPakE4bDZkbm4xWlJnYmVRWVZYRFZId2hFRnRzUk9vNkxNNmpDUDF3N0hjdk1xYWxWaWwwVUd3RmMvUm83QTNYQi9vcVE9PQ/cdn/77_reg.acelive</location><title>НТВ+ Кинохит</title></track><track xmlns="http://xspf.org/ns/0/"><location>http://content.torrent-tv.ru/bE5GNkFqajdjS2NYRDU4VTNLd3FtWmNIbnJpNzFNTE9kTko4T3RwR0I2NC9Xbm9ZNUw0Qmptd0xEVUM5V3JGWTk0bFJTOURmemMzQ0lrVUNQSnNHSWc9PQ/cdn/37_reg.acelive</location><title>НТВ+ Премьера</title></track><track xmlns="http://xspf.org/ns/0/"><location>http://content.torrent-tv.ru/QnNHQndPYWU2TGNWUnVCOThOMDZxeUVwY0JJYkV5OG1YVDNRYnBLZXZXRUFNODU4OHRDSFFrQzJreGRWZ1lDNWpKS2RNZEJFOWd0Q1dNOFFzclZ0VHc9PQ/cdn/81_reg.acelive</location><title>НТВ+ Наше новое кино</title></track><track xmlns="http://xspf.org/ns/0/"><location>http://content.torrent-tv.ru/cXRrYUFOendTTWpNaCtDNG45MTFMYXIzZ0tJK094Ly9YOEFSb3p2WmhlTlM3dUhndWcrYjJqWG16Y1B3bm9TWnhGK3p4blNBd3FqU0dEZStnZDR3NkE9PQ/cdn/35_reg.acelive</location><title>НТВ+ Киноклуб</title></track><track xmlns="http://xspf.org/ns/0/"><location>http://content.torrent-tv.ru/SkVDVDV4dTl1YWUxYitJdHJwTmplL2VkdWRuN282V1RQbmd5ek9QYVdSMGxoR0FvS2JpcE1XMFRKY2twb0JZU240cVd5RnYrclRHUVlKUnNYQ1Iwdnc9PQ/cdn/82_reg.acelive</location><title>НТВ+ Кино Союз</title></track><track xmlns="http://xspf.org/ns/0/"><location>http://content.torrent-tv.ru/SUxaNmU2aVhBbDMrUEkxK25HQ1VITkVYcEdySnJZUzBTMEEzZXdlL0VnWVJtYm1ib3FWSmZPR3lkUHBIZ0RFWUxXSStzRTMyQThvVENqeDV3VWQ2UkE9PQ/cdn/691_reg.acelive</location><title>НТВ+ 3D</title></track></trackList></playlist>
\ No newline at end of file