/*
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 *
 * 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 com.alibaba.nacos.core.cluster;

import com.alibaba.nacos.core.utils.ApplicationUtils;
import com.alibaba.nacos.core.utils.Loggers;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;

/**
 * @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
 */
public class MemberUtils {

	private static ServerMemberManager manager;

	public static void setManager(ServerMemberManager manager) {
		MemberUtils.manager = manager;
	}

	public static void copy(Member newMember, Member oldMember) {
		oldMember.setIp(newMember.getIp());
		oldMember.setPort(newMember.getPort());
		oldMember.setState(newMember.getState());
		oldMember.setExtendInfo(newMember.getExtendInfo());
		oldMember.setAddress(newMember.getAddress());
	}

	public static Member singleParse(String member) {
		int selfPort = ApplicationUtils.getPort();
		// Nacos default port is 8848
		int defaultPort = 8848;
		// Set the default Raft port information for security
		int defaultRaftPort = selfPort + 1000 >= 65535 ? selfPort + 1 : selfPort + 1000;

		String[] memberDetails = member.split("\\?");
		String address = memberDetails[0];
		int port = defaultPort;
		if (address.contains(":")) {
			String[] info = address.split(":");
			address = info[0];
			port = Integer.parseInt(info[1]);
		}

		// example ip:port?raft_port=&node_name=
		Map<String, String> extendInfo = new HashMap<>(4);

		if (memberDetails.length == 2) {
			String[] parameters = memberDetails[1].split("&");
			for (String parameter : parameters) {
				String[] info = parameter.split("=");
				extendInfo.put(info[0].trim(), info[1].trim());
			}
		}
		else {
			// The Raft Port information needs to be set by default
			extendInfo.put(MemberMetaDataConstants.RAFT_PORT,
					String.valueOf(defaultRaftPort));

		}
		return Member.builder().ip(address).port(port).extendInfo(extendInfo)
				.state(NodeState.UP).build();
	}

	public static Collection<Member> multiParse(Collection<String> addresses) {
		List<Member> members = new ArrayList<>(addresses.size());
		for (String address : addresses) {
			Member member = singleParse(address);
			members.add(member);
		}
		return members;
	}

	public static void onSuccess(Member member) {
		manager.getMemberAddressInfos().add(member.getAddress());
		member.setState(NodeState.UP);
		member.setFailAccessCnt(0);
		manager.update(member);
	}

	public static void onFail(Member member) {
		manager.getMemberAddressInfos().remove(member.getAddress());
		member.setState(NodeState.SUSPICIOUS);
		member.setFailAccessCnt(member.getFailAccessCnt() + 1);
		int maxFailAccessCnt = ApplicationUtils
				.getProperty("nacos.core.member.fail-access-cnt", Integer.class, 3);
		if (member.getFailAccessCnt() > maxFailAccessCnt) {
			member.setState(NodeState.DOWN);
		}
		manager.update(member);
	}

	public static void syncToFile(Collection<Member> members) {
		try {
			StringBuilder builder = new StringBuilder();
			for (String member : simpleMembers(members)) {
				builder.append(member).append(StringUtils.LF);
			}
			ApplicationUtils.writeClusterConf(builder.toString());
		}
		catch (Throwable ex) {
			Loggers.CLUSTER.error("Cluster member node persistence failed : {}", ex);
		}
	}

	@SuppressWarnings("PMD.UndefineMagicConstantRule")
	public static List<Member> kRandom(Collection<Member> members,
			Predicate<Member> filter) {
		int k = ApplicationUtils
				.getProperty("nacos.core.member.report.random-num", Integer.class, 3);

		List<Member> tmp = new ArrayList<>();
		// Here thinking similar consul gossip protocols random k node

		int totalSize = members.size();
		for (int i = 0; i < 3 * totalSize && members.size() <= k; i++) {
			for (Member member : members) {

				if (filter.test(member)) {
					tmp.add(member);
				}

			}
		}

		return tmp;
	}

	// 默认配置格式解析，只有nacos-server的ip or ip:port or hostname:port 信息
	// example 192.168.16.1:8848?raft_port=8849&key=value

	public static Collection<Member> readServerConf(Collection<String> members) {
		Set<Member> nodes = new HashSet<>();

		for (String member : members) {
			Member target = singleParse(member);
			nodes.add(target);
		}

		return nodes;
	}

	public static List<String> simpleMembers(Collection<Member> members) {
		return members.stream().map(member -> {
			String address = member.getAddress();
			StringBuilder params = new StringBuilder();
			String[] keys = MemberMetaDataConstants.META_KEY_LIST;
			int length = keys.length;
			for (int i = 0; i < length; i++) {
				params.append(keys[i]).append("=").append(member.getExtendVal(keys[i]));
				if (i != length - 1) {
					params.append("&");
				}
			}
			return address + "?" + params.toString();
		}).collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
	}

}

@@ -1111,16 +1111,63 @@
 tils %7B%0A%0A
+%09private static final String SEMICOLON = %22:%22;%0A%0A
 %09private
@@ -1203,16 +1203,16 @@
 anager;%0A
-
 %0A%09public
@@ -1592,24 +1592,76 @@
 ess());%0A%09%7D%0A%0A
+%09@SuppressWarnings(%22PMD.UndefineMagicConstantRule%22)%0A
 %09public stat
@@ -2076,19 +2076,25 @@
 ontains(
-%22:%22
+SEMICOLON
 )) %7B%0A%09%09%09
@@ -2123,19 +2123,25 @@
 s.split(
-%22:%22
+SEMICOLON
 );%0A%09%09%09ad

fix code style to adapter PMD