You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by nt...@apache.org on 2017/07/25 12:25:23 UTC
[08/16] cayenne git commit: CAY-2335: New XML loading/saving
mechanics with support of plugable handlers - new XML loader for DataMap -
new project version - updated test projects
http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DbEntityHandler.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DbEntityHandler.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DbEntityHandler.java
new file mode 100644
index 0000000..8de6bb1
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DbEntityHandler.java
@@ -0,0 +1,146 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.configuration.xml;
+
+import org.apache.cayenne.dba.TypesMapping;
+import org.apache.cayenne.exp.ExpressionFactory;
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.map.DbEntity;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * @since 4.1
+ */
+public class DbEntityHandler extends NamespaceAwareNestedTagHandler {
+
+ private static final String DB_ENTITY_TAG = "db-entity";
+ private static final String DB_ATTRIBUTE_TAG = "db-attribute";
+ private static final String DB_KEY_GENERATOR_TAG = "db-key-generator";
+ private static final String QUALIFIER_TAG = "qualifier";
+
+ private DataMap dataMap;
+ private DbEntity entity;
+ private DbAttribute lastAttribute;
+
+ public DbEntityHandler(NamespaceAwareNestedTagHandler parentHandler, DataMap dataMap) {
+ super(parentHandler);
+ this.dataMap = dataMap;
+ }
+
+ @Override
+ protected boolean processElement(String namespaceURI, String localName, Attributes attributes) throws SAXException {
+ switch (localName) {
+ case DB_ENTITY_TAG:
+ createDbEntity(attributes);
+ return true;
+
+ case DB_ATTRIBUTE_TAG:
+ createDbAttribute(attributes);
+ return true;
+
+ case QUALIFIER_TAG:
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ protected void processCharData(String localName, String data) {
+ switch (localName) {
+ case QUALIFIER_TAG:
+ createQualifier(data);
+ break;
+ }
+ }
+
+ @Override
+ protected ContentHandler createChildTagHandler(String namespaceURI, String localName, String qName, Attributes attributes) {
+ switch (localName) {
+ case DB_KEY_GENERATOR_TAG:
+ return new DbKeyGeneratorHandler(this, entity);
+ }
+ return super.createChildTagHandler(namespaceURI, localName, qName, attributes);
+ }
+
+ private void createDbEntity(Attributes attributes) {
+ String name = attributes.getValue("name");
+ entity = new DbEntity(name);
+ entity.setSchema(attributes.getValue("schema"));
+ entity.setCatalog(attributes.getValue("catalog"));
+ dataMap.addDbEntity(entity);
+ }
+
+ private void createDbAttribute(Attributes attributes) {
+ String name = attributes.getValue("name");
+ String type = attributes.getValue("type");
+
+ lastAttribute = new DbAttribute(name);
+ lastAttribute.setType(TypesMapping.getSqlTypeByName(type));
+ entity.addAttribute(lastAttribute);
+
+ String length = attributes.getValue("length");
+ if (length != null) {
+ lastAttribute.setMaxLength(Integer.parseInt(length));
+ }
+
+ String precision = attributes.getValue("attributePrecision");
+ if (precision != null) {
+ lastAttribute.setAttributePrecision(Integer.parseInt(precision));
+ }
+
+ // this is an obsolete 1.2 'precision' attribute that really meant 'scale'
+ String pseudoPrecision = attributes.getValue("precision");
+ if (pseudoPrecision != null) {
+ lastAttribute.setScale(Integer.parseInt(pseudoPrecision));
+ }
+
+ String scale = attributes.getValue("scale");
+ if (scale != null) {
+ lastAttribute.setScale(Integer.parseInt(scale));
+ }
+
+ lastAttribute.setPrimaryKey(DataMapHandler.TRUE.equalsIgnoreCase(attributes.getValue("isPrimaryKey")));
+ lastAttribute.setMandatory(DataMapHandler.TRUE.equalsIgnoreCase(attributes.getValue("isMandatory")));
+ lastAttribute.setGenerated(DataMapHandler.TRUE.equalsIgnoreCase(attributes.getValue("isGenerated")));
+ }
+
+ private void createQualifier(String qualifier) {
+ if (qualifier.trim().length() == 0) {
+ return;
+ }
+
+ // qualifier can belong to ObjEntity, DbEntity or a query
+ if (entity != null) {
+ entity.setQualifier(ExpressionFactory.exp(qualifier));
+ }
+ }
+
+ public DbEntity getEntity() {
+ return entity;
+ }
+
+ public DbAttribute getLastAttribute() {
+ return lastAttribute;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DbKeyGeneratorHandler.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DbKeyGeneratorHandler.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DbKeyGeneratorHandler.java
new file mode 100644
index 0000000..e76c4c3
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DbKeyGeneratorHandler.java
@@ -0,0 +1,117 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.configuration.xml;
+
+import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.map.DbKeyGenerator;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+/**
+ * @since 4.1
+ */
+public class DbKeyGeneratorHandler extends NamespaceAwareNestedTagHandler {
+
+ private static final String DB_KEY_GENERATOR_TAG = "db-key-generator";
+ private static final String DB_GENERATOR_TYPE_TAG = "db-generator-type";
+ private static final String DB_GENERATOR_NAME_TAG = "db-generator-name";
+ private static final String DB_KEY_CACHE_SIZE_TAG = "db-key-cache-size";
+
+ DbEntity entity;
+
+ public DbKeyGeneratorHandler(NamespaceAwareNestedTagHandler parentHandler, DbEntity entity) {
+ super(parentHandler);
+ this.entity = entity;
+ }
+
+ @Override
+ protected boolean processElement(String namespaceURI, String localName, Attributes attributes) throws SAXException {
+ switch (localName) {
+ case DB_KEY_GENERATOR_TAG:
+ createDbKeyGenerator();
+ return true;
+
+ case DB_GENERATOR_NAME_TAG:
+ case DB_GENERATOR_TYPE_TAG:
+ case DB_KEY_CACHE_SIZE_TAG:
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ protected void processCharData(String localName, String data) {
+ switch (localName) {
+ case DB_GENERATOR_TYPE_TAG:
+ setDbGeneratorType(data);
+ break;
+
+ case DB_GENERATOR_NAME_TAG:
+ setDbGeneratorName(data);
+ break;
+
+ case DB_KEY_CACHE_SIZE_TAG:
+ setDbKeyCacheSize(data);
+ break;
+ }
+ }
+
+ private void createDbKeyGenerator() {
+ entity.setPrimaryKeyGenerator(new DbKeyGenerator());
+ }
+
+ private void setDbGeneratorType(String type) {
+ if (entity == null) {
+ return;
+ }
+ DbKeyGenerator pkGenerator = entity.getPrimaryKeyGenerator();
+ pkGenerator.setGeneratorType(type);
+ if (pkGenerator.getGeneratorType() == null) {
+ entity.setPrimaryKeyGenerator(null);
+ }
+ }
+
+ private void setDbGeneratorName(String name) {
+ if (entity == null) {
+ return;
+ }
+ DbKeyGenerator pkGenerator = entity.getPrimaryKeyGenerator();
+ if (pkGenerator == null) {
+ return;
+ }
+ pkGenerator.setGeneratorName(name);
+ }
+
+ private void setDbKeyCacheSize(String size) {
+ if (entity == null) {
+ return;
+ }
+ DbKeyGenerator pkGenerator = entity.getPrimaryKeyGenerator();
+ if (pkGenerator == null) {
+ return;
+ }
+ try {
+ pkGenerator.setKeyCacheSize(new Integer(size.trim()));
+ } catch (Exception ex) {
+ pkGenerator.setKeyCacheSize(null);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DbRelationshipHandler.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DbRelationshipHandler.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DbRelationshipHandler.java
new file mode 100644
index 0000000..b4730e5
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DbRelationshipHandler.java
@@ -0,0 +1,97 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.configuration.xml;
+
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.map.DbJoin;
+import org.apache.cayenne.map.DbRelationship;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+/**
+ * @since 4.1
+ */
+public class DbRelationshipHandler extends NamespaceAwareNestedTagHandler {
+
+ private static final String DB_RELATIONSHIP_TAG = "db-relationship";
+ public static final String DB_ATTRIBUTE_PAIR_TAG = "db-attribute-pair";
+
+ private DataMap map;
+
+ private DbRelationship dbRelationship;
+
+ public DbRelationshipHandler(NamespaceAwareNestedTagHandler parentHandler, DataMap map) {
+ super(parentHandler);
+ this.map = map;
+ }
+
+ @Override
+ protected boolean processElement(String namespaceURI, String localName, Attributes attributes) throws SAXException {
+
+ switch (localName) {
+ case DB_RELATIONSHIP_TAG:
+ createRelationship(attributes);
+ return true;
+
+ case DB_ATTRIBUTE_PAIR_TAG:
+ createDbAttributePair(attributes);
+ return true;
+ }
+
+ return false;
+ }
+
+ private void createRelationship(Attributes attributes) throws SAXException {
+ String name = attributes.getValue("name");
+ if (name == null) {
+ throw new SAXException("DbRelationshipHandler::createRelationship() - missing \"name\" attribute.");
+ }
+
+ String sourceName = attributes.getValue("source");
+ if (sourceName == null) {
+ throw new SAXException("DbRelationshipHandler::createRelationship() - null source entity");
+ }
+
+ DbEntity source = map.getDbEntity(sourceName);
+ if (source == null) {
+ return;
+ }
+
+ dbRelationship = new DbRelationship(name);
+ dbRelationship.setSourceEntity(source);
+ dbRelationship.setTargetEntityName(attributes.getValue("target"));
+ dbRelationship.setToMany(DataMapHandler.TRUE.equalsIgnoreCase(attributes.getValue("toMany")));
+ dbRelationship.setToDependentPK(DataMapHandler.TRUE.equalsIgnoreCase(attributes.getValue("toDependentPK")));
+
+ source.addRelationship(dbRelationship);
+ }
+
+ private void createDbAttributePair(Attributes attributes) {
+ DbJoin join = new DbJoin(dbRelationship);
+ join.setSourceName(attributes.getValue("source"));
+ join.setTargetName(attributes.getValue("target"));
+ dbRelationship.addJoin(join);
+ }
+
+ public DbRelationship getDbRelationship() {
+ return dbRelationship;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DefaultDataChannelMetaData.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DefaultDataChannelMetaData.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DefaultDataChannelMetaData.java
new file mode 100644
index 0000000..869abed
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DefaultDataChannelMetaData.java
@@ -0,0 +1,91 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.configuration.xml;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.cayenne.configuration.ConfigurationNode;
+
+/**
+ * <p>
+ * Default implementation of {@link DataChannelMetaData} that stores data in Map.
+ * </p>
+ * <p>
+ * This implementation is thread safe.
+ * </p>
+ *
+ * @see NoopDataChannelMetaData
+ * @since 4.1
+ */
+public class DefaultDataChannelMetaData implements DataChannelMetaData {
+
+ private Map<ConfigurationNode, Map<Class<?>, Object>> map;
+
+ public DefaultDataChannelMetaData() {
+ map = new ConcurrentHashMap<>();
+ }
+
+ /**
+ * value.getClass() will be used under the hood to associate data with the key object.
+ *
+ * @param key object for which we want to store data
+ * @param value data to store
+ */
+ @Override
+ public void add(ConfigurationNode key, Object value) {
+ if(key == null || value == null) {
+ return;
+ }
+
+ Map<Class<?>, Object> data = map.get(key);
+ if(data == null) {
+ data = new ConcurrentHashMap<>();
+ Map<Class<?>, Object> old = map.put(key, data);
+ // extra check in case if someone was fast enough
+ if(old != null) {
+ data.putAll(old);
+ }
+ }
+ data.put(value.getClass(), value);
+ }
+
+ /**
+ * If either key or value is {@code null} then {@code null} will be returned.
+ *
+ * @param key object for wich we want meta data
+ * @param type meta data type class
+ * @param <T> data type
+ * @return value or {@code null}
+ */
+ @Override
+ public <T> T get(ConfigurationNode key, Class<T> type) {
+ if(key == null || type == null) {
+ return null;
+ }
+
+ Map<Class<?>, Object> data = map.get(key);
+ if(data == null) {
+ return null;
+ }
+
+ return type.cast(data.get(type));
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DefaultHandlerFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DefaultHandlerFactory.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DefaultHandlerFactory.java
new file mode 100644
index 0000000..5fd2293
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DefaultHandlerFactory.java
@@ -0,0 +1,44 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.configuration.xml;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.Attributes;
+
+/**
+ * @since 4.1
+ */
+public class DefaultHandlerFactory implements HandlerFactory {
+
+ private static Logger logger = LoggerFactory.getLogger(XMLDataChannelDescriptorLoader.class);
+
+ @Override
+ public NamespaceAwareNestedTagHandler createHandler(String namespace, String localName, NamespaceAwareNestedTagHandler parent) {
+ return new NamespaceAwareNestedTagHandler(parent, namespace) {
+ @Override
+ protected boolean processElement(String namespaceURI, String localName, Attributes attributes) {
+ logger.debug("Skipping unknown tag <{}:{}>", namespaceURI, localName);
+ return true;
+ }
+ };
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/EmbeddableAttributeHandler.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/EmbeddableAttributeHandler.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/EmbeddableAttributeHandler.java
new file mode 100644
index 0000000..d0d4ae9
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/EmbeddableAttributeHandler.java
@@ -0,0 +1,69 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.configuration.xml;
+
+import org.apache.cayenne.map.EmbeddedAttribute;
+import org.apache.cayenne.map.ObjEntity;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+/**
+ * @since 4.1
+ */
+public class EmbeddableAttributeHandler extends NamespaceAwareNestedTagHandler {
+
+ private static final String EMBEDDED_ATTRIBUTE_TAG = "embedded-attribute";
+ private static final String EMBEDDABLE_ATTRIBUTE_OVERRIDE_TAG = "embeddable-attribute-override";
+
+ private ObjEntity entity;
+
+ private EmbeddedAttribute embeddedAttribute;
+
+ public EmbeddableAttributeHandler(NamespaceAwareNestedTagHandler parentHandler, ObjEntity entity) {
+ super(parentHandler);
+ this.entity = entity;
+ }
+
+ @Override
+ protected boolean processElement(String namespaceURI, String localName, Attributes attributes) throws SAXException {
+ switch (localName) {
+ case EMBEDDED_ATTRIBUTE_TAG:
+ createEmbeddableAttribute(attributes);
+ return true;
+
+ case EMBEDDABLE_ATTRIBUTE_OVERRIDE_TAG:
+ createEmbeddableAttributeOverride(attributes);
+ return true;
+ }
+
+ return false;
+ }
+
+ private void createEmbeddableAttribute(Attributes attributes) {
+ embeddedAttribute = new EmbeddedAttribute(attributes.getValue("name"));
+ embeddedAttribute.setType(attributes.getValue("type"));
+ entity.addAttribute(embeddedAttribute);
+ }
+
+ private void createEmbeddableAttributeOverride(Attributes attributes) {
+ embeddedAttribute.addAttributeOverride(attributes.getValue("name"),
+ attributes.getValue("db-attribute-path"));
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/EmbeddableHandler.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/EmbeddableHandler.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/EmbeddableHandler.java
new file mode 100644
index 0000000..648474e
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/EmbeddableHandler.java
@@ -0,0 +1,75 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.configuration.xml;
+
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.Embeddable;
+import org.apache.cayenne.map.EmbeddableAttribute;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+/**
+ * @since 4.1
+ */
+public class EmbeddableHandler extends NamespaceAwareNestedTagHandler {
+
+ private static final String EMBEDDABLE_TAG = "embeddable";
+ private static final String EMBEDDABLE_ATTRIBUTE_TAG = "embeddable-attribute";
+
+ private DataMap map;
+
+ private Embeddable embeddable;
+
+ public EmbeddableHandler(NamespaceAwareNestedTagHandler parentHandler, DataMap map) {
+ super(parentHandler);
+ this.map = map;
+ }
+
+ @Override
+ protected boolean processElement(String namespaceURI, String localName, Attributes attributes) throws SAXException {
+ switch (localName) {
+ case EMBEDDABLE_TAG:
+ createEmbeddable(attributes);
+ return true;
+
+ case EMBEDDABLE_ATTRIBUTE_TAG:
+ createEmbeddableAttribute(attributes);
+ return true;
+ }
+
+ return false;
+ }
+
+ private void createEmbeddable(Attributes attributes) {
+ embeddable = new Embeddable(attributes.getValue("className"));
+ map.addEmbeddable(embeddable);
+ }
+
+ private void createEmbeddableAttribute(Attributes attributes) {
+ EmbeddableAttribute ea = new EmbeddableAttribute(attributes.getValue("name"));
+ ea.setType(attributes.getValue("type"));
+ ea.setDbAttributeName(attributes.getValue("db-attribute-name"));
+ embeddable.addAttribute(ea);
+ }
+
+ public Embeddable getEmbeddable() {
+ return embeddable;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/HandlerFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/HandlerFactory.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/HandlerFactory.java
new file mode 100644
index 0000000..d02be06
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/HandlerFactory.java
@@ -0,0 +1,30 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.configuration.xml;
+
+/**
+ * Factory that creates handlers for unparsed elements.
+ *
+ * @since 4.1
+ */
+public interface HandlerFactory {
+
+ NamespaceAwareNestedTagHandler createHandler(String namespace, String localName, NamespaceAwareNestedTagHandler parent);
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/LoaderContext.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/LoaderContext.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/LoaderContext.java
new file mode 100644
index 0000000..9507e4f
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/LoaderContext.java
@@ -0,0 +1,63 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.configuration.xml;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.cayenne.map.DataMap;
+import org.xml.sax.XMLReader;
+
+/**
+ * @since 4.1
+ */
+public class LoaderContext {
+
+ Collection<DataMapLoaderListener> dataMapListeners;
+
+ private XMLReader xmlReader;
+
+ private HandlerFactory factory;
+
+ public LoaderContext(XMLReader reader, HandlerFactory factory) {
+ this.xmlReader = reader;
+ this.factory = factory;
+ dataMapListeners = new ArrayList<>();
+ }
+
+ public HandlerFactory getFactory() {
+ return factory;
+ }
+
+ public XMLReader getXmlReader() {
+ return xmlReader;
+ }
+
+ public void addDataMapListener(DataMapLoaderListener dataMapLoaderListener) {
+ dataMapListeners.add(dataMapLoaderListener);
+ }
+
+ public void dataMapLoaded(DataMap dataMap) {
+ for(DataMapLoaderListener listener : dataMapListeners) {
+ listener.onDataMapLoaded(dataMap);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/NamespaceAwareNestedTagHandler.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/NamespaceAwareNestedTagHandler.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/NamespaceAwareNestedTagHandler.java
new file mode 100644
index 0000000..ec332e5
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/NamespaceAwareNestedTagHandler.java
@@ -0,0 +1,97 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.configuration.xml;
+
+import java.util.Objects;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * Base class for handlers that can delegate execution of unknown tags to
+ * handlers produced by factory.
+ *
+ * @since 4.1
+ */
+abstract public class NamespaceAwareNestedTagHandler extends SAXNestedTagHandler {
+
+ protected String targetNamespace;
+
+ private StringBuilder charactersBuffer = new StringBuilder();
+
+ public NamespaceAwareNestedTagHandler(LoaderContext loaderContext) {
+ super(loaderContext);
+ }
+
+ public NamespaceAwareNestedTagHandler(SAXNestedTagHandler parentHandler, String targetNamespace) {
+ super(parentHandler);
+ this.targetNamespace = Objects.requireNonNull(targetNamespace);
+ }
+
+ public NamespaceAwareNestedTagHandler(NamespaceAwareNestedTagHandler parentHandler) {
+ this(parentHandler, parentHandler.targetNamespace);
+ }
+
+ abstract protected boolean processElement(String namespaceURI, String localName, Attributes attributes) throws SAXException;
+
+ protected void processCharData(String localName, String data) {
+ }
+
+ @Override
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ charactersBuffer.append(ch, start, length);
+ }
+
+ @Override
+ public final void startElement(String namespaceURI, String localName,
+ String qName, Attributes attributes) throws SAXException {
+
+ ContentHandler childHandler = createChildTagHandler(namespaceURI, localName, qName, attributes);
+
+ if(!namespaceURI.equals(targetNamespace) || !processElement(namespaceURI, localName, attributes)) {
+ // recursively pass element down into child handlers
+ childHandler.startElement(namespaceURI, localName, qName, attributes);
+ }
+
+ // push child handler to the stack...
+ loaderContext.getXmlReader().setContentHandler(childHandler);
+ charactersBuffer.delete(0, charactersBuffer.length());
+ }
+
+ @Override
+ public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
+ super.endElement(namespaceURI, localName, qName);
+ if(namespaceURI.equals(targetNamespace) && parentHandler instanceof NamespaceAwareNestedTagHandler) {
+ ((NamespaceAwareNestedTagHandler)parentHandler).processCharData(localName, charactersBuffer.toString());
+ }
+ }
+
+ @Override
+ protected ContentHandler createChildTagHandler(String namespaceURI, String localName,
+ String qName, Attributes attributes) {
+ // try to pass unknown tags to someone else
+ return loaderContext.getFactory().createHandler(namespaceURI, localName, this);
+ }
+
+ public void setTargetNamespace(String targetNamespace) {
+ this.targetNamespace = targetNamespace;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/NoopDataChannelMetaData.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/NoopDataChannelMetaData.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/NoopDataChannelMetaData.java
new file mode 100644
index 0000000..cbcb696
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/NoopDataChannelMetaData.java
@@ -0,0 +1,42 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+package org.apache.cayenne.configuration.xml;
+
+import org.apache.cayenne.configuration.ConfigurationNode;
+
+/**
+ * Noop implementation of {@link DataChannelMetaData}.
+ * Used by Cayenne runtime by default as it doesn't need this information.
+ *
+ * @see DefaultDataChannelMetaData
+ * @since 4.1
+ */
+public class NoopDataChannelMetaData implements DataChannelMetaData {
+
+ @Override
+ public void add(ConfigurationNode key, Object value) {
+ // noop
+ }
+
+ @Override
+ public <T> T get(ConfigurationNode key, Class<T> type) {
+ return null;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/ObjEntityHandler.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/ObjEntityHandler.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/ObjEntityHandler.java
new file mode 100644
index 0000000..e9ee9aa
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/ObjEntityHandler.java
@@ -0,0 +1,210 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.configuration.xml;
+
+import org.apache.cayenne.exp.ExpressionFactory;
+import org.apache.cayenne.map.CallbackDescriptor;
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.ObjAttribute;
+import org.apache.cayenne.map.ObjEntity;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * @since 4.1
+ */
+public class ObjEntityHandler extends NamespaceAwareNestedTagHandler {
+
+ private static final String OBJ_ENTITY_TAG = "obj-entity";
+ private static final String OBJ_ATTRIBUTE_TAG = "obj-attribute";
+ private static final String OBJ_ATTRIBUTE_OVERRIDE_TAG = "attribute-override";
+ private static final String EMBEDDED_ATTRIBUTE_TAG = "embedded-attribute";
+ private static final String QUALIFIER_TAG = "qualifier";
+
+ // lifecycle listeners and callbacks related
+ private static final String POST_ADD_TAG = "post-add";
+ private static final String PRE_PERSIST_TAG = "pre-persist";
+ private static final String POST_PERSIST_TAG = "post-persist";
+ private static final String PRE_UPDATE_TAG = "pre-update";
+ private static final String POST_UPDATE_TAG = "post-update";
+ private static final String PRE_REMOVE_TAG = "pre-remove";
+ private static final String POST_REMOVE_TAG = "post-remove";
+ private static final String POST_LOAD_TAG = "post-load";
+
+ private DataMap map;
+
+ private ObjEntity entity;
+
+ private ObjAttribute lastAttribute;
+
+ public ObjEntityHandler(NamespaceAwareNestedTagHandler parentHandler, DataMap map) {
+ super(parentHandler);
+ this.map = map;
+ }
+
+ @Override
+ protected boolean processElement(String namespaceURI, String localName, Attributes attributes) throws SAXException {
+ switch (localName) {
+ case OBJ_ENTITY_TAG:
+ createObjEntity(attributes);
+ return true;
+
+ case OBJ_ATTRIBUTE_TAG:
+ createObjAttribute(attributes);
+ return true;
+
+ case OBJ_ATTRIBUTE_OVERRIDE_TAG:
+ processStartAttributeOverride(attributes);
+ return true;
+
+ case QUALIFIER_TAG:
+ return true;
+
+ case POST_ADD_TAG:
+ case PRE_PERSIST_TAG:
+ case POST_PERSIST_TAG:
+ case PRE_UPDATE_TAG:
+ case POST_UPDATE_TAG:
+ case PRE_REMOVE_TAG:
+ case POST_REMOVE_TAG:
+ case POST_LOAD_TAG:
+ createCallback(localName, attributes);
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ protected ContentHandler createChildTagHandler(String namespaceURI, String localName, String qName, Attributes attributes) {
+ if(namespaceURI.equals(targetNamespace)) {
+ switch (localName) {
+ case EMBEDDED_ATTRIBUTE_TAG:
+ return new EmbeddableAttributeHandler(this, entity);
+ }
+ }
+
+ return super.createChildTagHandler(namespaceURI, localName, qName, attributes);
+ }
+
+ @Override
+ protected void processCharData(String localName, String data) {
+ switch (localName) {
+ case QUALIFIER_TAG:
+ createQualifier(data);
+ break;
+ }
+ }
+
+ private void createObjEntity(Attributes attributes) {
+ entity = new ObjEntity(attributes.getValue("name"));
+ entity.setClassName(attributes.getValue("className"));
+ entity.setClientClassName(attributes.getValue("clientClassName"));
+ entity.setAbstract(DataMapHandler.TRUE.equalsIgnoreCase(attributes.getValue("abstract")));
+ entity.setReadOnly(DataMapHandler.TRUE.equalsIgnoreCase(attributes.getValue("readOnly")));
+ entity.setServerOnly(DataMapHandler.TRUE.equalsIgnoreCase(attributes.getValue("serverOnly")));
+ if ("optimistic".equals(attributes.getValue("", "lock-type"))) {
+ entity.setDeclaredLockType(ObjEntity.LOCK_TYPE_OPTIMISTIC);
+ }
+
+ String superEntityName = attributes.getValue("superEntityName");
+ if (superEntityName != null) {
+ entity.setSuperEntityName(superEntityName);
+ } else {
+ entity.setSuperClassName(attributes.getValue("superClassName"));
+ entity.setClientSuperClassName(attributes.getValue("clientSuperClassName"));
+ }
+ entity.setDbEntityName(attributes.getValue("dbEntityName"));
+
+ map.addObjEntity(entity);
+ }
+
+ private void createObjAttribute(Attributes attributes) {
+ String dbPath = attributes.getValue("db-attribute-path");
+ if (dbPath == null) {
+ dbPath = attributes.getValue("db-attribute-name");
+ }
+
+ lastAttribute = new ObjAttribute(attributes.getValue("name"));
+ lastAttribute.setType(attributes.getValue("type"));
+ lastAttribute.setUsedForLocking(DataMapHandler.TRUE.equalsIgnoreCase(attributes.getValue("lock")));
+ lastAttribute.setDbAttributePath(dbPath);
+ entity.addAttribute(lastAttribute);
+ }
+
+ private void processStartAttributeOverride(Attributes attributes) {
+ entity.addAttributeOverride(attributes.getValue("name"),
+ attributes.getValue("db-attribute-path"));
+ }
+
+ private CallbackDescriptor getCallbackDescriptor(String type) {
+ if (entity == null) {
+ return null;
+ }
+
+ switch (type) {
+ case POST_ADD_TAG:
+ return entity.getCallbackMap().getPostAdd();
+ case PRE_PERSIST_TAG:
+ return entity.getCallbackMap().getPrePersist();
+ case POST_PERSIST_TAG:
+ return entity.getCallbackMap().getPostPersist();
+ case PRE_UPDATE_TAG:
+ return entity.getCallbackMap().getPreUpdate();
+ case POST_UPDATE_TAG:
+ return entity.getCallbackMap().getPostUpdate();
+ case PRE_REMOVE_TAG:
+ return entity.getCallbackMap().getPreRemove();
+ case POST_REMOVE_TAG:
+ return entity.getCallbackMap().getPostRemove();
+ case POST_LOAD_TAG:
+ return entity.getCallbackMap().getPostLoad();
+ }
+
+ return null;
+ }
+
+ private void createCallback(String type, Attributes attributes) {
+ String methodName = attributes.getValue("method-name");
+ CallbackDescriptor descriptor = getCallbackDescriptor(type);
+ if(descriptor != null) {
+ descriptor.addCallbackMethod(methodName);
+ }
+ }
+
+ private void createQualifier(String qualifier) {
+ if (qualifier.trim().length() == 0) {
+ return;
+ }
+
+ if (entity != null) {
+ entity.setDeclaredQualifier(ExpressionFactory.exp(qualifier));
+ }
+ }
+
+ public ObjEntity getEntity() {
+ return entity;
+ }
+
+ public ObjAttribute getLastAttribute() {
+ return lastAttribute;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/ObjRelationshipHandler.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/ObjRelationshipHandler.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/ObjRelationshipHandler.java
new file mode 100644
index 0000000..63be65d
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/ObjRelationshipHandler.java
@@ -0,0 +1,109 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.configuration.xml;
+
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.DeleteRule;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.map.ObjRelationship;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+/**
+ * @since 4.1
+ */
+public class ObjRelationshipHandler extends NamespaceAwareNestedTagHandler {
+
+ public static final String OBJ_RELATIONSHIP_TAG = "obj-relationship";
+
+ @Deprecated
+ public static final String DB_RELATIONSHIP_REF_TAG = "db-relationship-ref";
+
+ private DataMap map;
+
+ private ObjRelationship objRelationship;
+
+ public ObjRelationshipHandler(NamespaceAwareNestedTagHandler parentHandler, DataMap map) {
+ super(parentHandler);
+ this.map = map;
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ protected boolean processElement(String namespaceURI, String localName, Attributes attributes) throws SAXException {
+ switch (localName) {
+ case OBJ_RELATIONSHIP_TAG:
+ addObjRelationship(attributes);
+ return true;
+
+ case DB_RELATIONSHIP_REF_TAG:
+ addDbRelationshipRef(attributes);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * <db-relationship-ref> tag deprecated
+ */
+ @Deprecated
+ private void addDbRelationshipRef(Attributes attributes) throws SAXException {
+ String name = attributes.getValue("name");
+ if (name == null) {
+ throw new SAXException("ObjRelationshipHandler::addDbRelationshipRef() - null DbRelationship name for "
+ + objRelationship.getName());
+ }
+
+ String path = objRelationship.getDbRelationshipPath();
+ objRelationship.setDbRelationshipPath((path != null) ? path + "." + name : name);
+ }
+
+ private void addObjRelationship(Attributes attributes) throws SAXException {
+ String name = attributes.getValue("name");
+ if (null == name) {
+ throw new SAXException("ObjRelationshipHandler::addObjRelationship() - unable to parse target.");
+ }
+
+ String sourceName = attributes.getValue("source");
+ if (sourceName == null) {
+ throw new SAXException("ObjRelationshipHandler::addObjRelationship() - unable to parse source.");
+ }
+
+ ObjEntity source = map.getObjEntity(sourceName);
+ if (source == null) {
+ throw new SAXException("ObjRelationshipHandler::addObjRelationship() - unable to find source " + sourceName);
+ }
+
+ objRelationship = new ObjRelationship(name);
+ objRelationship.setSourceEntity(source);
+ objRelationship.setTargetEntityName(attributes.getValue("target"));
+ objRelationship.setDeleteRule(DeleteRule.deleteRuleForName(attributes.getValue("deleteRule")));
+ objRelationship.setUsedForLocking(DataMapHandler.TRUE.equalsIgnoreCase(attributes.getValue("lock")));
+ objRelationship.setDeferredDbRelationshipPath((attributes.getValue("db-relationship-path")));
+ objRelationship.setCollectionType(attributes.getValue("collection-type"));
+ objRelationship.setMapKey(attributes.getValue("map-key"));
+ source.addRelationship(objRelationship);
+ }
+
+ public ObjRelationship getObjRelationship() {
+ return objRelationship;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/ProcedureHandler.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/ProcedureHandler.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/ProcedureHandler.java
new file mode 100644
index 0000000..72e6a53
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/ProcedureHandler.java
@@ -0,0 +1,114 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.configuration.xml;
+
+import org.apache.cayenne.dba.TypesMapping;
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.Procedure;
+import org.apache.cayenne.map.ProcedureParameter;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+/**
+ * @since 4.1
+ */
+public class ProcedureHandler extends NamespaceAwareNestedTagHandler {
+
+ private static final String PROCEDURE_TAG = "procedure";
+ private static final String PROCEDURE_PARAMETER_TAG = "procedure-parameter";
+
+ private DataMap map;
+
+ private Procedure procedure;
+
+ public ProcedureHandler(NamespaceAwareNestedTagHandler parentHandler, DataMap map) {
+ super(parentHandler);
+ this.map = map;
+ }
+
+ @Override
+ protected boolean processElement(String namespaceURI, String localName, Attributes attributes) throws SAXException {
+ switch (localName) {
+ case PROCEDURE_TAG:
+ addProcedure(attributes);
+ return true;
+
+ case PROCEDURE_PARAMETER_TAG:
+ addProcedureParameter(attributes);
+ return true;
+ }
+
+ return false;
+ }
+
+ private void addProcedure(Attributes attributes) throws SAXException{
+ String name = attributes.getValue("name");
+ String returningValue = attributes.getValue("returningValue");
+ if (null == name) {
+ throw new SAXException("ProcedureHandler::addProcedure() - no procedure name.");
+ }
+
+ procedure = new Procedure(name);
+ procedure.setReturningValue(returningValue != null && returningValue.equalsIgnoreCase(DataMapHandler.TRUE));
+ procedure.setSchema(attributes.getValue("schema"));
+ procedure.setCatalog(attributes.getValue("catalog"));
+ map.addProcedure(procedure);
+ }
+
+ private void addProcedureParameter(Attributes attributes) throws SAXException {
+
+ String name = attributes.getValue("name");
+ if (name == null) {
+ throw new SAXException("ProcedureHandler::addProcedureParameter() - no procedure parameter name.");
+ }
+
+ ProcedureParameter parameter = new ProcedureParameter(name);
+
+ String type = attributes.getValue("type");
+ if (type != null) {
+ parameter.setType(TypesMapping.getSqlTypeByName(type));
+ }
+
+ String length = attributes.getValue("length");
+ if (length != null) {
+ parameter.setMaxLength(Integer.parseInt(length));
+ }
+
+ String precision = attributes.getValue("precision");
+ if (precision != null) {
+ parameter.setPrecision(Integer.parseInt(precision));
+ }
+
+ String direction = attributes.getValue("direction");
+ if ("in".equals(direction)) {
+ parameter.setDirection(ProcedureParameter.IN_PARAMETER);
+ } else if ("out".equals(direction)) {
+ parameter.setDirection(ProcedureParameter.OUT_PARAMETER);
+ } else if ("in_out".equals(direction)) {
+ parameter.setDirection(ProcedureParameter.IN_OUT_PARAMETER);
+ }
+
+ procedure.addCallParameter(parameter);
+ }
+
+ public Procedure getProcedure() {
+ return procedure;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/QueryDescriptorHandler.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/QueryDescriptorHandler.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/QueryDescriptorHandler.java
new file mode 100644
index 0000000..4034184
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/QueryDescriptorHandler.java
@@ -0,0 +1,191 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.configuration.xml;
+
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.QueryDescriptor;
+import org.apache.cayenne.map.QueryDescriptorLoader;
+import org.apache.cayenne.util.Util;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+/**
+ * @since 4.1
+ */
+public class QueryDescriptorHandler extends NamespaceAwareNestedTagHandler {
+
+ private static final String QUERY_DESCRIPTOR_TAG = "query";
+ private static final String QUERY_SQL_TAG = "sql";
+ private static final String QUERY_EJBQL_TAG = "ejbql";
+ private static final String QUERY_QUALIFIER_TAG = "qualifier";
+ private static final String QUERY_ORDERING_TAG = "ordering";
+ private static final String QUERY_PREFETCH_TAG = "prefetch";
+
+ public static final String PROPERTY_TAG = "property";
+
+ private DataMap map;
+
+ private QueryDescriptorLoader queryBuilder;
+ private QueryDescriptor descriptor;
+ private boolean changed;
+
+ private String sqlKey;
+ private String descending;
+ private String ignoreCase;
+
+ public QueryDescriptorHandler(NamespaceAwareNestedTagHandler parentHandler, DataMap map) {
+ super(parentHandler);
+ this.map = map;
+ }
+
+ @Override
+ protected boolean processElement(String namespaceURI, String localName, Attributes attributes) throws SAXException {
+
+ switch (localName) {
+ case QUERY_DESCRIPTOR_TAG:
+ addQueryDescriptor(attributes);
+ return true;
+
+ case PROPERTY_TAG:
+ addQueryDescriptorProperty(attributes);
+ return true;
+
+ case QUERY_SQL_TAG:
+ this.sqlKey = attributes.getValue("adapter-class");
+ return true;
+
+ case QUERY_ORDERING_TAG:
+ createQueryOrdering(attributes);
+ return true;
+
+ case QUERY_EJBQL_TAG:
+ case QUERY_QUALIFIER_TAG:
+ case QUERY_PREFETCH_TAG:
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ protected void processCharData(String localName, String data) {
+ switch (localName) {
+ case QUERY_SQL_TAG:
+ queryBuilder.addSql(data, sqlKey);
+ break;
+
+ case QUERY_EJBQL_TAG:
+ queryBuilder.setEjbql(data);
+ break;
+
+ case QUERY_QUALIFIER_TAG:
+ createQualifier(data);
+ break;
+
+ case QUERY_ORDERING_TAG:
+ addQueryOrdering(data);
+ break;
+
+ case QUERY_PREFETCH_TAG:
+ queryBuilder.addPrefetch(data);
+ break;
+ }
+ }
+
+ @Override
+ protected void beforeScopeEnd() {
+ map.addQueryDescriptor(getQueryDescriptor());
+ }
+
+ private void addQueryDescriptor(Attributes attributes) throws SAXException {
+ String name = attributes.getValue("name");
+ if (null == name) {
+ throw new SAXException("QueryDescriptorHandler::addQueryDescriptor() - no query name.");
+ }
+
+ queryBuilder = new QueryDescriptorLoader();
+ queryBuilder.setName(name);
+
+ String type = attributes.getValue("type");
+ // Legacy format support (v7 and older)
+ if(type == null) {
+ queryBuilder.setLegacyFactory(attributes.getValue("factory"));
+ } else {
+ queryBuilder.setQueryType(type);
+ }
+
+ String rootName = attributes.getValue("root-name");
+ queryBuilder.setRoot(map, attributes.getValue("root"), rootName);
+
+ // TODO: Andrus, 2/13/2006 'result-type' is only used in ProcedureQuery
+ // and is deprecated in 1.2
+ String resultEntity = attributes.getValue("result-entity");
+ if (!Util.isEmptyString(resultEntity)) {
+ queryBuilder.setResultEntity(resultEntity);
+ }
+
+ changed = true;
+ }
+
+ private void addQueryDescriptorProperty(Attributes attributes) throws SAXException {
+ String name = attributes.getValue("name");
+ if (null == name) {
+ throw new SAXException("QueryDescriptorHandler::addQueryDescriptorProperty() - no property name.");
+ }
+
+ String value = attributes.getValue("value");
+ if (null == value) {
+ throw new SAXException("QueryDescriptorHandler::addQueryDescriptorProperty() - no property value.");
+ }
+
+ queryBuilder.addProperty(name, value);
+ changed = true;
+ }
+
+ private void createQualifier(String qualifier) {
+ if (qualifier.trim().length() == 0) {
+ return;
+ }
+
+ queryBuilder.setQualifier(qualifier);
+ changed = true;
+ }
+
+ private void createQueryOrdering(Attributes attributes) {
+ descending = attributes.getValue("descending");
+ ignoreCase = attributes.getValue("ignore-case");
+ }
+
+ private void addQueryOrdering(String path) {
+ queryBuilder.addOrdering(path, descending, ignoreCase);
+ changed = true;
+ }
+
+ public QueryDescriptor getQueryDescriptor() {
+ if(queryBuilder == null) {
+ return null;
+ }
+ if(descriptor == null || changed) {
+ descriptor = queryBuilder.buildQueryDescriptor();
+ changed = false;
+ }
+ return descriptor;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/RootDataMapHandler.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/RootDataMapHandler.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/RootDataMapHandler.java
new file mode 100644
index 0000000..dba5022
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/RootDataMapHandler.java
@@ -0,0 +1,51 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.configuration.xml;
+
+import org.apache.cayenne.map.DataMap;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * @since 4.1
+ */
+public class RootDataMapHandler extends NamespaceAwareNestedTagHandler {
+
+ public RootDataMapHandler(LoaderContext loaderContext) {
+ super(loaderContext);
+ setTargetNamespace(DataMap.SCHEMA_XSD);
+ }
+
+ @Override
+ protected boolean processElement(String namespaceURI, String localName, Attributes attributes) throws SAXException {
+ return false;
+ }
+
+ @Override
+ protected ContentHandler createChildTagHandler(String namespaceURI, String localName, String qName, Attributes attributes) {
+ if(targetNamespace.equals(namespaceURI) && "data-map".equals(localName)) {
+ return new DataMapHandler(this);
+ }
+
+ return super.createChildTagHandler(namespaceURI, localName, qName, attributes);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/SAXNestedTagHandler.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/SAXNestedTagHandler.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/SAXNestedTagHandler.java
new file mode 100644
index 0000000..d2d4e04
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/SAXNestedTagHandler.java
@@ -0,0 +1,175 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+package org.apache.cayenne.configuration.xml;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * A superclass of nested tag handlers for parsing of XML documents with SAX.
+ * This class is not namespace aware, i.e. tags like <info:property/> and <property/>
+ * will be treated as equal.
+ * Use {@link NamespaceAwareNestedTagHandler} if you need to process namespaces.
+ *
+ * @see NamespaceAwareNestedTagHandler
+ * @since 3.1
+ * @since 4.1 redesigned and moved from {@link org.apache.cayenne.configuration} package
+ */
+public class SAXNestedTagHandler extends DefaultHandler {
+
+ private final static Locator NOOP_LOCATOR = new Locator() {
+
+ public int getColumnNumber() {
+ return -1;
+ }
+
+ public int getLineNumber() {
+ return -1;
+ }
+
+ public String getPublicId() {
+ return "<unknown>";
+ }
+
+ public String getSystemId() {
+ return "<unknown>";
+ }
+ };
+
+ protected LoaderContext loaderContext;
+ protected ContentHandler parentHandler;
+ protected Locator locator;
+
+ public SAXNestedTagHandler(LoaderContext loaderContext) {
+ this.loaderContext = Objects.requireNonNull(loaderContext);
+ this.locator = NOOP_LOCATOR;
+ }
+
+ public SAXNestedTagHandler(SAXNestedTagHandler parentHandler) {
+ this.parentHandler = Objects.requireNonNull(parentHandler);
+ this.loaderContext = Objects.requireNonNull(parentHandler.loaderContext);
+
+ locator = parentHandler.locator;
+ if (locator == null) {
+ locator = NOOP_LOCATOR;
+ }
+ }
+
+ protected String unexpectedTagMessage(String tagFound, String... tagsExpected) {
+
+ List<String> expected = tagsExpected != null
+ ? Arrays.asList(tagsExpected)
+ : Collections.<String> emptyList();
+
+ return String.format("tag <%s> is unexpected at [%d,%d]. The following tags are allowed here: %s",
+ tagFound,
+ locator.getColumnNumber(),
+ locator.getLineNumber(),
+ expected);
+ }
+
+ /**
+ * Main method to process XML content.
+ * Should be override in subclasses, by default do nothing.
+ * Return value should be true if tag was fully processed and shouldn't be passed down to child handler.
+ *
+ * @param namespaceURI namespace for tag
+ * @param localName tag local name (i.e. w/o namespace prefix)
+ * @param attributes tag attributes
+ *
+ * @return true if tag was processed
+ *
+ * @throws SAXException can be thrown to abort parsing
+ *
+ * @see #createChildTagHandler(String, String, String, Attributes)
+ */
+ protected boolean processElement(String namespaceURI, String localName, Attributes attributes) throws SAXException {
+ return true;
+ }
+
+ /**
+ * Callback method that is called before this handler pushed out of parsers stack.
+ * Can be used to flush some aggregate state.
+ */
+ protected void beforeScopeEnd() {
+ }
+
+ /**
+ * This method should be used to create nested handlers to process children elements.
+ * This method should never return {@code null}.
+ *
+ * @param namespaceURI namespace for tag
+ * @param localName tag local name (i.e. w/o namespace prefix)
+ * @param qName tag full name (i.e. with namespace prefix)
+ * @param attributes tag attributes
+ * @return new handler to process child tag
+ */
+ protected ContentHandler createChildTagHandler(String namespaceURI, String localName,
+ String qName, Attributes attributes) {
+ // loose handling of unrecognized tags - just ignore them
+ return new SAXNestedTagHandler(this);
+ }
+
+ protected void stop() {
+ beforeScopeEnd();
+ // pop self from the handler stack
+ loaderContext.getXmlReader().setContentHandler(parentHandler);
+ }
+
+ /**
+ * This method directly called by SAX parser, do not override it directly,
+ * use {@link #processElement(String, String, Attributes)} method instead to process content.
+ *
+ * @see #createChildTagHandler(String, String, String, Attributes)
+ */
+ @Override
+ public void startElement(String namespaceURI, String localName,
+ String qName, Attributes attributes) throws SAXException {
+ ContentHandler childHandler = createChildTagHandler(namespaceURI, localName, qName, attributes);
+
+ if(!processElement(namespaceURI, localName, attributes)) {
+ childHandler.startElement(namespaceURI, localName, qName, attributes);
+ }
+
+ // push child handler to the stack...
+ loaderContext.getXmlReader().setContentHandler(childHandler);
+ }
+
+ @Override
+ public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
+ stop();
+ }
+
+ @Override
+ public void setDocumentLocator(Locator locator) {
+ this.locator = locator;
+ }
+
+ public ContentHandler getParentHandler() {
+ return parentHandler;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/XMLDataChannelDescriptorLoader.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/XMLDataChannelDescriptorLoader.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/XMLDataChannelDescriptorLoader.java
new file mode 100644
index 0000000..e9d104e
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/XMLDataChannelDescriptorLoader.java
@@ -0,0 +1,149 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+package org.apache.cayenne.configuration.xml;
+
+import org.apache.cayenne.ConfigurationException;
+import org.apache.cayenne.configuration.ConfigurationNameMapper;
+import org.apache.cayenne.configuration.ConfigurationTree;
+import org.apache.cayenne.configuration.DataChannelDescriptor;
+import org.apache.cayenne.configuration.DataChannelDescriptorLoader;
+import org.apache.cayenne.configuration.DataMapLoader;
+import org.apache.cayenne.di.AdhocObjectFactory;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.resource.Resource;
+import org.apache.cayenne.util.Util;
+import org.slf4j.LoggerFactory;
+import org.slf4j.Logger;
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLReader;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+
+/**
+ * @since 3.1
+ * @since 4.1 moved from org.apache.cayenne.configuration package
+ */
+public class XMLDataChannelDescriptorLoader implements DataChannelDescriptorLoader {
+
+ private static Logger logger = LoggerFactory.getLogger(XMLDataChannelDescriptorLoader.class);
+
+ static final String CURRENT_PROJECT_VERSION = "10";
+
+ /**
+ * @deprecated the caller should use password resolving strategy instead of
+ * resolving the password on the spot. For one thing this can be
+ * used in the Modeler and no password may be available.
+ */
+ @Deprecated
+ static String passwordFromURL(URL url) {
+ InputStream inputStream;
+ String password = null;
+
+ try {
+ inputStream = url.openStream();
+ password = passwordFromInputStream(inputStream);
+ } catch (IOException exception) {
+ // Log the error while trying to open the stream. A null
+ // password will be returned as a result.
+ logger.warn(exception.getMessage(), exception);
+ }
+
+ return password;
+ }
+
+ /**
+ * @deprecated the caller should use password resolving strategy instead of
+ * resolving the password on the spot. For one thing this can be
+ * used in the Modeler and no password may be available.
+ */
+ @Deprecated
+ static String passwordFromInputStream(InputStream inputStream) {
+ String password = null;
+
+ try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));) {
+
+ password = bufferedReader.readLine();
+ } catch (IOException exception) {
+ logger.warn(exception.getMessage(), exception);
+ } finally {
+
+ try {
+ inputStream.close();
+ } catch (IOException ignored) {
+ }
+ }
+
+ return password;
+ }
+
+ @Inject
+ protected DataMapLoader dataMapLoader;
+
+ @Inject
+ protected ConfigurationNameMapper nameMapper;
+
+ @Inject
+ protected AdhocObjectFactory objectFactory;
+
+ @Inject
+ protected HandlerFactory handlerFactory;
+
+ @Override
+ public ConfigurationTree<DataChannelDescriptor> load(Resource configurationResource) throws ConfigurationException {
+
+ if (configurationResource == null) {
+ throw new NullPointerException("Null configurationResource");
+ }
+
+ URL configurationURL = configurationResource.getURL();
+
+ logger.info("Loading XML configuration resource from " + configurationURL);
+
+ final DataChannelDescriptor descriptor = new DataChannelDescriptor();
+ descriptor.setConfigurationSource(configurationResource);
+ descriptor.setName(nameMapper.configurationNodeName(DataChannelDescriptor.class, configurationResource));
+
+ try(InputStream in = configurationURL.openStream()) {
+ XMLReader parser = Util.createXmlReader();
+ LoaderContext loaderContext = new LoaderContext(parser, handlerFactory);
+ loaderContext.addDataMapListener(new DataMapLoaderListener() {
+ @Override
+ public void onDataMapLoaded(DataMap dataMap) {
+ descriptor.getDataMaps().add(dataMap);
+ }
+ });
+
+ DataChannelHandler rootHandler = new DataChannelHandler(this, descriptor, loaderContext);
+ parser.setContentHandler(rootHandler);
+ parser.setErrorHandler(rootHandler);
+ parser.parse(new InputSource(in));
+ } catch (Exception e) {
+ throw new ConfigurationException("Error loading configuration from %s", e, configurationURL);
+ }
+
+ // TODO: andrus 03/10/2010 - actually provide load failures here...
+ return new ConfigurationTree<>(descriptor, null);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/XMLDataMapLoader.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/XMLDataMapLoader.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/XMLDataMapLoader.java
new file mode 100644
index 0000000..eb82d32
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/XMLDataMapLoader.java
@@ -0,0 +1,102 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+package org.apache.cayenne.configuration.xml;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.configuration.DataMapLoader;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.resource.Resource;
+import org.apache.cayenne.util.Util;
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLReader;
+
+import java.io.InputStream;
+
+/**
+ * @since 3.1
+ * @since 4.1 moved from org.apache.cayenne.configuration package
+ */
+public class XMLDataMapLoader implements DataMapLoader {
+
+ private static final String DATA_MAP_LOCATION_SUFFIX = ".map.xml";
+
+ @Inject
+ protected HandlerFactory handlerFactory;
+
+ private DataMap map;
+
+ public synchronized DataMap load(Resource configurationResource) throws CayenneRuntimeException {
+ try(InputStream in = configurationResource.getURL().openStream()) {
+ XMLReader parser = Util.createXmlReader();
+ LoaderContext loaderContext = new LoaderContext(parser, handlerFactory);
+ loaderContext.addDataMapListener(new DataMapLoaderListener() {
+ @Override
+ public void onDataMapLoaded(DataMap dataMap) {
+ map = dataMap;
+ }
+ });
+ RootDataMapHandler rootHandler = new RootDataMapHandler(loaderContext);
+
+ parser.setContentHandler(rootHandler);
+ parser.setErrorHandler(rootHandler);
+ parser.parse(new InputSource(in));
+ } catch (Exception e) {
+ throw new CayenneRuntimeException("Error loading configuration from %s", e, configurationResource.getURL());
+ }
+
+ if(map == null) {
+ throw new CayenneRuntimeException("Unable to load data map from %s", configurationResource.getURL());
+ }
+
+ if(map.getName() == null) {
+ // set name based on location if no name provided by map itself
+ map.setName(mapNameFromLocation(configurationResource.getURL().getFile()));
+ }
+ return map;
+ }
+
+ /**
+ * Helper method to guess the map name from its location.
+ */
+ protected String mapNameFromLocation(String location) {
+ if (location == null) {
+ return "Untitled";
+ }
+
+ int lastSlash = location.lastIndexOf('/');
+ if (lastSlash < 0) {
+ lastSlash = location.lastIndexOf('\\');
+ }
+
+ if (lastSlash >= 0 && lastSlash + 1 < location.length()) {
+ location = location.substring(lastSlash + 1);
+ }
+
+ if (location.endsWith(DATA_MAP_LOCATION_SUFFIX)) {
+ location = location.substring(0, location.length() - DATA_MAP_LOCATION_SUFFIX.length());
+ }
+
+ return location;
+ }
+
+ public void setHandlerFactory(HandlerFactory handlerFactory) {
+ this.handlerFactory = handlerFactory;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/conn/DataSourceInfo.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/conn/DataSourceInfo.java b/cayenne-server/src/main/java/org/apache/cayenne/conn/DataSourceInfo.java
index f51c8f0..0647fbc 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/conn/DataSourceInfo.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/conn/DataSourceInfo.java
@@ -21,6 +21,7 @@ package org.apache.cayenne.conn;
import java.io.Serializable;
+import org.apache.cayenne.configuration.ConfigurationNodeVisitor;
import org.apache.cayenne.configuration.PasswordEncoding;
import org.apache.cayenne.configuration.PlainTextPasswordEncoder;
import org.apache.cayenne.di.DIRuntimeException;
@@ -134,58 +135,46 @@ public class DataSourceInfo implements Cloneable, Serializable, XMLSerializable
/**
* @since 3.1
*/
- public void encodeAsXML(XMLEncoder encoder) {
- encoder.println("<data-source>");
- encoder.indent(1);
-
- encoder.print("<driver");
- encoder.printAttribute("value", jdbcDriver);
- encoder.println("/>");
+ @Override
+ public void encodeAsXML(XMLEncoder encoder, ConfigurationNodeVisitor delegate) {
+ encoder.start("data-source");
- encoder.print("<url");
- encoder.printAttribute("value", dataSourceUrl);
- encoder.println("/>");
+ encoder.start("driver").attribute("value", jdbcDriver).end();
+ encoder.start("url").attribute("value", dataSourceUrl).end();
- encoder.print("<connectionPool");
- encoder.printAttribute("min", String.valueOf(minConnections));
- encoder.printAttribute("max", String.valueOf(maxConnections));
- encoder.println("/>");
+ encoder.start("connectionPool")
+ .attribute("min", minConnections)
+ .attribute("max", String.valueOf(maxConnections))
+ .end();
- encoder.print("<login");
- encoder.printAttribute("userName", userName);
+ encoder.start("login").attribute("userName", userName);
if (DataSourceInfo.PASSWORD_LOCATION_MODEL.equals(passwordLocation)) {
-
PasswordEncoding passwordEncoder = getPasswordEncoder();
-
if (passwordEncoder != null) {
String passwordEncoded = passwordEncoder.encodePassword(password, passwordEncoderKey);
- encoder.printAttribute("password", passwordEncoded);
+ encoder.attribute("password", passwordEncoded);
}
}
if (!PlainTextPasswordEncoder.class.getName().equals(passwordEncoderClass)) {
- encoder.printAttribute("encoderClass", passwordEncoderClass);
+ encoder.attribute("encoderClass", passwordEncoderClass);
}
- encoder.printAttribute("encoderKey", passwordEncoderKey);
+ encoder.attribute("encoderKey", passwordEncoderKey);
if (!DataSourceInfo.PASSWORD_LOCATION_MODEL.equals(passwordLocation)) {
- encoder.printAttribute("passwordLocation", passwordLocation);
+ encoder.attribute("passwordLocation", passwordLocation);
}
// TODO: this is very not nice... we need to clean up the whole
- // DataSourceInfo
- // to avoid returning arbitrary labels...
+ // DataSourceInfo to avoid returning arbitrary labels...
String passwordSource = getPasswordSource();
if (!"Not Applicable".equals(passwordSource)) {
- encoder.printAttribute("passwordSource", passwordSource);
+ encoder.attribute("passwordSource", passwordSource);
}
- encoder.println("/>");
-
- encoder.indent(-1);
- encoder.println("</data-source>");
+ encoder.end().end();
}
public DataSourceInfo cloneInfo() {
http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/exp/Expression.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/Expression.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/Expression.java
index 0d35cea..77a8cdf 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/Expression.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/Expression.java
@@ -31,6 +31,7 @@ import java.util.List;
import java.util.Map;
import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.configuration.ConfigurationNodeVisitor;
import org.apache.cayenne.exp.parser.ASTScalar;
import org.apache.cayenne.util.ConversionUtil;
import org.apache.cayenne.util.HashCodeBuilder;
@@ -679,14 +680,15 @@ public abstract class Expression implements Serializable, XMLSerializable {
*
* @since 1.1
*/
- public void encodeAsXML(XMLEncoder encoder) {
- encoder.print("<![CDATA[");
+ @Override
+ public void encodeAsXML(XMLEncoder encoder, ConfigurationNodeVisitor delegate) {
+ StringBuilder sb = new StringBuilder();
try {
- appendAsString(encoder.getPrintWriter());
+ appendAsString(sb);
} catch (IOException e) {
throw new CayenneRuntimeException("Unexpected IO exception appending to PrintWriter", e);
}
- encoder.print("]]>");
+ encoder.cdata(sb.toString(), true);
}
/**